Launch a Linux Server and Automate
Launch an EC2 Linux server via CLI and write Bash scripts to automate sysadmin tasks.
Introduction
30 Second Summary
Every website you visit and every app on your phone runs on a server somewhere. Most of those servers run Linux.
In this project, you will launch a Linux server on AWS and connect to it remotely. You will also write two Bash scripts that automate real system administration tasks.
What You'll Build
You will launch and operate an Amazon Linux 2023 EC2 instance over SSH, then write Bash scripts to automate system health checks and log analysis.
By the end of this project, you'll have:
- A running EC2 instance launched entirely from the AWS CLI.
- Hands-on experience navigating Linux, managing files, users, permissions, and packages.
- Two working Bash scripts that automate system administration tasks.
- Secret Mission: Write a backup script that archives files and uploads them to S3.
Are there any prerequisites?
This project assumes you have an AWS account with Free Tier access and the AWS CLI installed and configured on your Windows computer. You will use PowerShell as your local terminal throughout the project.
Create a Key Pair and Launch Your EC2 Instance
Every cloud engineer needs to know how to launch a server from the command line. The AWS Console is useful for browsing, but the AWS CLI is what you will use in real automation workflows.
In this step, you will create an SSH key pair, launch an Amazon Linux 2023 EC2 instance, and connect to it. All from Windows PowerShell.
In this step, get ready to:
- Create an SSH key pair and save the private key file.
- Launch an AL2023 t2.micro EC2 instance using the AWS CLI.
- Connect to your instance via SSH from PowerShell.
Create an SSH key pair using the AWS CLI
To connect to a Linux server, you need an SSH key pair. AWS stores the public half. You keep the private half as a .pem file on your local machine.
- Press the Windows key to open your search bar.
- Type PowerShell and press Enter to open it.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your key pair and save the private key file by running this command:
aws ec2 create-key-pair --key-name my-ec2-key --query 'KeyMaterial' --output text | out-file -encoding ascii -filepath my-ec2-key.pem
What does this command do?
`aws ec2 create-key-pair` tells AWS to generate a new key pair named my-ec2-key. The --query 'KeyMaterial' flag extracts just the private key content from the response.
The pipe into out-file -encoding ascii saves it as a properly encoded .pem file. This encoding step is critical on Windows. PowerShell's default > redirect uses UTF-8 encoding, which corrupts the key file and breaks SSH.
- Confirm the file was created by running:
ls my-ec2-key.pem
You should see my-ec2-key.pem listed with a file size greater than 0.
Don't see the file?
Make sure you ran the command from your Desktop directory. Run pwd to check your current location. If you are somewhere else, run cd ~/Desktop first and try the create-key-pair command again.
If you see an error about the key name already existing, a key pair named my-ec2-key already exists in your AWS account. Delete it first with aws ec2 delete-key-pair --key-name my-ec2-key and try again.
Help me troubleshoot my key pair creation.
Now you need to lock down the file permissions. SSH refuses to use a private key that other users on your machine can read.
- Restrict permissions on your key file by running these three commands one at a time:
# Remove inherited permissions from the file
icacls .\my-ec2-key.pem /inheritance:r
# Grant read-only access to your current user
icacls .\my-ec2-key.pem /grant:r "${env:USERNAME}:(R)"
# Remove access for other common groups
icacls .\my-ec2-key.pem /remove "Authenticated Users" "Users" "Everyone"
What do these commands do?
`icacls` is the Windows command for managing file permissions. The first command strips all inherited permissions from the file. The second grants read-only access to your Windows user account.
The third removes any remaining access for other user groups. The result is that only your user can read the .pem file. This is the Windows equivalent of chmod 400 on Linux.
Seeing 'No mapping between account names and security IDs'?
This usually means your Windows username contains special characters or your account is a Microsoft account with an email-style name. Try replacing ${env:USERNAME} with your actual local username in quotes. You can find it by running echo $env:USERNAME in PowerShell.
Help me fix my key file permissions.
Launch your EC2 instance
With your key pair ready, you can launch a server. You will use an SSM parameter that automatically resolves to the latest Amazon Linux 2023 AMI so you never have to hunt for an AMI ID.
- Launch your EC2 instance by running this command:
aws ec2 run-instances `
--image-id resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 `
--instance-type t2.micro `
--key-name my-ec2-key `
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=my-linux-server}]'
What does this command do?
`--image-id resolve:ssm:...` tells AWS to look up the latest Amazon Linux 2023 AMI automatically instead of hardcoding an AMI ID that could go stale.
`--instance-type t2.micro` picks a small, Free Tier eligible instance with 1 vCPU and 1 GiB of RAM. --key-name my-ec2-key associates the key pair you just created so you can SSH in. --tag-specifications gives your instance the name my-linux-server so you can find it easily in the AWS Console.
You should see a large JSON response. Look for the InstanceId field near the top. It looks like i-0abc123def456789.
- Copy the InstanceId value. You will need it in the next commands.
Why use the AWS CLI instead of the console?
Clicking through the EC2 console works, but it is not repeatable or automatable. The CLI command you just ran could be pasted into a script, a CI/CD pipeline, or an Infrastructure as Code tool with zero changes.
CLI fluency is a core requirement for AWS Cloud Engineer roles. Every command you learn here translates directly into real automation workflows.
Seeing an error about permissions or authorization?
If you see UnauthorizedOperation, your AWS CLI credentials may not have permission to launch EC2 instances. Make sure your IAM user has the AmazonEC2FullAccess policy attached.
If you see an error about the key pair not being found, double-check that the --key-name value matches exactly. Run aws ec2 describe-key-pairs to list your existing key pairs.
Help me debug my EC2 instance launch.
- Wait for the instance to reach the running state by running this command (replace the instance ID with your own):
aws ec2 wait instance-running --instance-ids [[INSTANCE_ID="your instance ID e.g. i-0abc123def456789"]]
This command pauses until your instance finishes booting. It returns silently when the instance is ready. This takes about 30 to 60 seconds.
Seeing a 'Waiter encountered a terminal failure state' error?
This means the instance failed to start. Common causes include an invalid key pair name, insufficient permissions, or your account not having a default VPC. Double-check the instance ID matches what you copied from the previous command.
Help me debug my EC2 instance launch failure.
Connect to your instance via SSH
Your instance is running. Now you need its public IP address so you can connect to it.
- Retrieve the public IP address by running this command (use your own instance ID):
aws ec2 describe-instances `
--instance-ids [[INSTANCE_ID="your instance ID e.g. i-0abc123def456789"]] `
--query 'Reservations[0].Instances[0].PublicIpAddress' `
--output text
What does this command do?
`describe-instances` fetches details about your instance. The --query flag uses JMESPath syntax to extract just the public IP address from the response instead of printing the entire JSON output.
You should see an IP address printed to the screen, something like 3.85.192.47. Copy this value.
- Connect to your instance via SSH by running this command (replace the IP with your own):
ssh -i .\my-ec2-key.pem ec2-user@[[EC2_PUBLIC_IP="your instance public IP address"]]
What does this command do?
`ssh -i` tells SSH to use a specific private key file for authentication. ec2-user is the default username on Amazon Linux 2023 instances. The IP address tells SSH which server to connect to.
- SSH will ask if you want to continue connecting because the server's fingerprint is unknown. Type yes and press Enter.
Your terminal prompt changes to something like [ec2-user@ip-172-31-xx-xx ~]$. You are now inside your Linux server.
Getting 'Permission denied (publickey)'?
This means SSH cannot authenticate with your private key. Check these common causes:
- Make sure you saved the key with out-file -encoding ascii. If you used > instead, delete the file and recreate the key pair.
- Verify the file permissions are correct by running icacls .\my-ec2-key.pem. Only your username should appear with (R) access.
- Double-check that you specified --key-name my-ec2-key when launching the instance. If you used a different key name, the key file will not match.
Help me fix my SSH connection to EC2.
You now have a live Linux server running in the cloud and a direct terminal connection to it. Next up, you will explore the Linux filesystem and learn to manage files like a sysadmin.
Explore the Linux Filesystem and Manage Files
Your EC2 instance is running and you have an active SSH session.
Every Linux server follows a standard directory structure. Configuration files, logs, and user data all live in predictable locations. In this step, you'll navigate that structure, set up your own project directory, and learn how Linux controls file access through permissions.
In this step, get ready to:
- Explore key Linux directories like /etc, /var/log, and /home.
- Create a project directory with files using standard Linux commands.
- Understand and modify file permissions with chmod and chown.
Navigate the Linux filesystem
Linux organizes everything into a single directory tree that starts at / (the root). Every file, device, and configuration on the server lives somewhere under this tree.
- Check where you are on the server by running this command:
pwd
You should see /home/ec2-user. This is your home directory, the default landing spot when you SSH in.
- List everything in your home directory, including hidden files, by running:
ls -la
You should see a few files starting with . (dot files). These are hidden configuration files that Linux uses behind the scenes.
Now let's explore some key directories that every Linux server has.
- Peek inside the system configuration directory by running:
ls /etc | head -20
- Check what log files are available by running:
ls /var/log
- See which user home directories exist by running:
ls /home
- Check the temporary files directory by running:
ls /tmp
What are these directories?
- /etc stores system-wide configuration files. Think of it as the server's settings folder.
- /var/log holds log files. When something goes wrong, this is where you look first.
- /home contains a personal directory for each user on the server.
- /tmp is for temporary files that any user or program can write to. Files here are automatically cleaned up on reboot.
Seeing each directory one at a time is useful, but a visual map of the entire filesystem is even better. The tree command does exactly that. It is not installed by default, so you need to install it first.
- Install tree using the dnf package manager by running:
sudo dnf install tree -y
What is dnf?
dnf is the package manager for Amazon Linux 2023. It installs, updates, and removes software on your server. The -y flag auto-confirms the installation so you don't have to type "yes" manually.
Seeing a "permission denied" error?
Make sure your command starts with sudo. Installing packages requires administrator privileges.
Still stuck? Help me troubleshoot this dnf installation error on my EC2 instance.
- Visualize the top level of the entire Linux filesystem by running:
tree -L 1 /
You should see a tree diagram showing all the top-level directories like /etc, /home, /var, and more. The -L 1 flag limits the output to one level deep so it stays readable.
- Return to your home directory by running:
cd ~
Create a working directory and manage files
Now that you know your way around, it is time to create your own workspace. You will build a project directory, practice creating and editing files, and learn how to redirect command output into files.
- Create your project directory with subdirectories by running this command:
mkdir -p ~/project/scripts ~/project/logs ~/project/backup
What does the -p flag do?
The -p flag tells mkdir to create parent directories as needed. Without it, the command would fail because ~/project doesn't exist yet.
- Confirm the directory structure was created by running:
tree ~/project
You should see a tree with project at the top and three subdirectories: backup, logs, and scripts.
Now let's create some files. Linux gives you several ways to write content into files.
- Create an empty file using touch by running:
touch ~/project/notes.txt
- Write a line of text into the file using the > redirection operator by running:
echo "Hello Linux" > ~/project/notes.txt
- Read the file's contents by running:
cat ~/project/notes.txt
You should see Hello Linux printed to your terminal.
What does > do?
The > operator redirects output to a file. It completely overwrites whatever was in the file before. If the file doesn't exist, it creates one.
What if you want to add content without erasing what's already there? That's what >> is for.
- Append a second line to the file by running:
echo "This is my EC2 instance" >> ~/project/notes.txt
- Verify both lines are in the file by running:
cat ~/project/notes.txt
You should see both lines: Hello Linux on the first line and This is my EC2 instance on the second.
What is the difference between > and >>?
- > overwrites the entire file with new content.
- >> appends to the end of the file, keeping everything that was already there.
Sometimes you need to make more complex edits than a single line. The nano text editor lets you edit files interactively, right in your terminal.
- Open notes.txt in the nano editor by running:
nano ~/project/notes.txt
A full-screen text editor opens inside your terminal. You should see your two lines of text.
- Use your arrow keys to move to the end of the file.
- Type a new line: Edited with nano.
- Press Ctrl+O to save the file.
- Press Enter to confirm the filename.
- Press Ctrl+X to exit nano.
- Verify your edit was saved by running:
cat ~/project/notes.txt
You should now see three lines, with Edited with nano at the bottom.
Nano keyboard shortcuts
Nano displays its available shortcuts at the bottom of the screen. The ^ symbol means Ctrl. So ^O means Ctrl+O (save) and ^X means Ctrl+X (exit).
Finally, let's practice deleting a file. Cleaning up is just as important as creating.
- Create a temporary file by running:
touch ~/project/temp.txt
- Delete the file using rm by running:
rm ~/project/temp.txt
- Confirm the file is gone by running:
ls ~/project
You should see the three subdirectories (backup, logs, scripts) and notes.txt. The temp.txt file is gone.
Be careful with rm
Linux does not have a recycle bin. When you rm a file, it is permanently deleted. Always double-check the file path before pressing Enter.
Understand and modify file permissions
Linux is a multi-user operating system. Permissions control exactly who can read, write, or execute each file. This system is the foundation of Linux security.
- View the detailed file listing for your project directory by running:
ls -la ~/project/
You should see a table with columns for permissions, owner, group, size, date, and name. The leftmost column is the permissions string.
How do I read the permissions string?
A permissions string like -rw-r--r-- has four parts.
- The first character is the file type. - means a regular file. d means a directory.
- Characters 2-4 (rw-) are the owner's permissions: read, write, no execute.
- Characters 5-7 (r--) are the group's permissions: read only.
- Characters 8-10 (r--) are everyone else's permissions: read only.
Instead of typing rwx letters, Linux lets you use numbers to set permissions. Each permission has a value: read = 4, write = 2, execute = 1. You add them together to get a three-digit code.
Common permission numbers
- 755 = owner can read, write, and execute. Everyone else can read and execute. Used for scripts you want others to run.
- 644 = owner can read and write. Everyone else can only read. This is the default for most files.
- 700 = owner gets full access. Nobody else can touch the file at all.
- Make notes.txt executable by setting permissions to 755 by running:
chmod 755 ~/project/notes.txt
- Verify the permission change by running:
ls -la ~/project/notes.txt
You should see -rwxr-xr-x at the start of the line. The x characters mean the file is now executable.
- Reset the permissions back to the normal default (644) by running:
chmod 644 ~/project/notes.txt
- Confirm the permissions are back to normal by running:
ls -la ~/project/notes.txt
You should see -rw-r--r-- again. No more execute permission.
Permissions control what you can do with a file. Ownership controls who those permissions apply to. The chown command changes a file's owner.
- Change the owner of notes.txt to the root user by running:
sudo chown root ~/project/notes.txt
- Verify the ownership change by running:
ls -la ~/project/notes.txt
The owner column should now show root instead of ec2-user. Only sudo can change ownership to another user.
- Change ownership back to your user by running:
sudo chown ec2-user ~/project/notes.txt
Getting "Operation not permitted"?
The chown command requires sudo to change file ownership. Make sure you included sudo at the start of the command.
Still stuck? Help me fix this chown permission error on my EC2 instance.
You now know how to navigate a Linux server, create and manage files, and control who can access them. Next up, you'll learn about users, processes, and packages to take full control of your server.
Manage Users, Processes, and Packages
In the previous step, you explored the Linux filesystem, created files and directories, and learned how permissions control access. You now know your way around the server's folder structure.
A real server does not just sit there holding files. It runs processes, hosts user accounts, and needs software updates. In this step, you will create a user account, inspect what your server is running, and learn to install and update software packages.
In this step, get ready to:
- Create a new Linux user and explore how the system tracks accounts.
- Inspect running processes and learn to find, monitor, and stop them.
- Install, search, and update software packages with the dnf package manager.
Create a new user and explore user accounts
Linux is a multi-user operating system. Every server can have multiple user accounts, each with different permissions. Understanding how users work is essential for controlling who can access what on your server.
- Make sure you are connected to your EC2 instance via SSH as ec2-user.
- Create a new user called testuser by running this command:
sudo useradd testuser
- Set a password for the new user by running:
sudo passwd testuser
What will I see?
You will be prompted to type a new password twice. The characters will not appear on screen as you type. This is normal Linux security behavior.
Pick something simple like testpass123 since this is just for practice. You will need this password in a moment.
Linux stores information about every user account in a file called /etc/passwd. Each line represents one user.
- View all user accounts on the system by running:
cat /etc/passwd
What is /etc/passwd?
Each line follows the format username:x:UID:GID:comment:home:shell. For example, ec2-user has a home directory at /home/ec2-user and uses /bin/bash as its shell.
Despite the name, this file does not store actual passwords. Passwords live in a separate, protected file called /etc/shadow.
- Scroll to the bottom of the output. You should see testuser listed as the last entry.
- View the groups on your system by running:
cat /etc/group
What are Linux groups?
Groups let you manage permissions for multiple users at once. When you created testuser, Linux automatically created a matching group with the same name. You should see testuser at the bottom of the group list.
Now try switching to the new user account to see the server from their perspective.
- Switch to testuser by running:
su - testuser
- Type the password you set earlier when prompted.
- Confirm which user you are by running:
whoami
- You should see testuser in the output.
- Switch back to ec2-user by running:
exit
What is the difference between su and sudo?
The su command switches your entire session to another user. You stay as that user until you type exit.
The sudo command runs a single command as the root (admin) user, then drops back to your normal account. On this server, ec2-user has sudo privileges by default. That is why you can run sudo useradd without issues.
su command asks for a password you did not set?
Make sure you are typing the password you set with sudo passwd testuser, not your AWS password. If you forgot it, run sudo passwd testuser again to set a new one.
Help me troubleshoot switching users with su on my EC2 instance.
Inspect and manage running processes
Your server is always running processes in the background. These include the SSH service that keeps your connection alive, system logging, and more. Knowing how to see and control these processes is a core sysadmin skill.
- View all running processes by running:
ps aux
What does ps aux show?
- ps stands for "process status."
- a shows processes from all users.
- u formats the output in a user-friendly way with columns like CPU and memory usage.
- x includes background processes that are not attached to a terminal.
That list is a snapshot. To watch processes in real time, you can use top.
- Launch the real-time process viewer by running:
top
- Watch the display update every few seconds. You can see CPU usage, memory usage, and which processes are consuming the most resources.
- Press q to quit and return to your terminal.
There is a better version of top called htop that adds color coding and easier navigation. It does not come pre-installed, so you will install it now.
- Install htop by running:
sudo dnf install htop -y
- Launch htop by running:
htop
- You should see a colorful dashboard with CPU and memory bars at the top. Each row is a running process.
- Press q to quit.
When you have a long process list, you often need to find a specific one. You can pipe the output of ps aux through grep to filter results.
- Find the SSH service process by running:
ps aux | grep sshd
- You should see one or more lines containing sshd. This is the background service keeping your SSH connection alive.
Now practice stopping a process. You will create a harmless background process, find its ID, and kill it.
- Start a background sleep process by running:
sleep 300 &
- You should see output like [1] 12345. The number after the space is the PID (process ID). Note it down.
- Confirm the process is running by finding it with grep:
ps aux | grep sleep
- Stop the process by running this command, replacing the PID with the number from your output:
kill [[PID="the PID number from the sleep output above"]]
- Verify it is gone by running ps aux | grep sleep again. You should see only the grep command itself in the results, not the sleep process.
Some processes run as background services that start automatically when the server boots. Linux manages these with a tool called systemctl.
- Check the status of the SSH service by running:
sudo systemctl status sshd
- You should see active (running) in green text. This confirms the SSH daemon is healthy and handling your connection.
- Press q to exit the status view.
What is systemctl?
The systemctl command manages services. These are long-running background processes that start at boot and keep running until the server shuts down. Common examples include SSH, web servers, and database servers.
You can start, stop, restart, and enable services with systemctl. For now, systemctl status is the most useful. It tells you whether a service is running and shows recent log entries.
Not seeing active (running) for sshd?
If the SSH service were stopped, you would not be connected to the instance right now. If you see an error, make sure you typed sshd (with the d at the end) and not just ssh.
Help me troubleshoot systemctl on my AL2023 instance.
Search, install, and update packages with dnf
You already installed htop with a quick dnf install command. Now you will learn to use the package manager more thoroughly.
What is dnf?
Amazon Linux 2023 uses dnf as its default package manager. A package manager downloads, installs, updates, and removes software from official repositories. It handles dependencies automatically so you do not have to track them yourself.
The dnf tool is the successor to yum. The older yum command still works on AL2023 as an alias, but dnf is the current standard.
- Search for the tree package by running:
sudo dnf search tree
- You should see a list of packages with "tree" in their name or description. The one you want is simply called tree.
- Install tree by running:
sudo dnf install tree -y
What does the -y flag do?
The -y flag automatically answers "yes" to the confirmation prompt. Without it, dnf asks you to confirm before installing. In scripts and automation, -y keeps things moving without manual input.
- Verify tree works by running it against your project directory:
tree ~/project
- You should see a visual tree diagram of the directories and files inside ~/project. This is much easier to read than listing each folder manually.
The last thing a responsible server administrator does is keep the system's packages up to date. Security patches and bug fixes arrive through package updates.
- Update all installed packages to their latest versions by running:
sudo dnf upgrade -y
- This may take a minute or two. When it finishes, you should see a summary of how many packages were upgraded, installed, or already up to date.
Why upgrade regularly?
Running sudo dnf upgrade is how Linux servers stay patched against security vulnerabilities. In a production environment, this is one of the most important maintenance tasks. Unpatched servers are a common target for attackers.
Seeing a "Permission denied" error?
Make sure every dnf command starts with sudo. Installing and updating packages requires root privileges. Also confirm you are logged in as ec2-user (not testuser) by running whoami.
Help me fix a permission denied error with dnf on AL2023.
You now know how to manage users, monitor processes, and keep your server's software up to date. These are the daily operations that keep a Linux server healthy. Next up, you will put all of these skills together by writing real Bash scripts that automate system administration tasks.
Write Your Bash Scripts
Your Linux server is set up and you know your way around the filesystem, users, and packages. Now it's time to put those skills to work with automation.
In this step, you'll write two Bash scripts that automate real sysadmin tasks. The first reports your server's health. The second analyzes log files to surface errors and warnings.
In this step, get ready to:
- Write a health check script that reports system vitals like CPU, memory, and disk usage.
- Write a log analyzer script that parses log files for errors and warnings.
- Test both scripts against real data on your EC2 instance.
Create the health check script
A health check script is the first thing a cloud engineer reaches for when something seems off with a server. Instead of running five separate commands to check uptime, memory, and disk, you run one script that gives you the full picture.
- Make sure you're inside your project directory on the EC2 instance by running:
cd ~/project
- Open a new file called health-check.sh in nano by running:
nano health-check.sh
- Type or paste the following script into nano:
#!/bin/bash
# health-check.sh - System Health Check Script
# Reports key system vitals for a Linux server
echo "========================================"
echo " SYSTEM HEALTH CHECK REPORT"
echo " Generated: $(date)"
echo "========================================"
echo ""
echo "--- HOSTNAME ---"
hostname
echo ""
echo "--- UPTIME ---"
uptime
echo ""
echo "--- CPU INFO ---"
echo "CPU cores: $(nproc)"
echo "Load averages: $(cat /proc/loadavg | awk '{print $1, $2, $3}')"
echo ""
echo "--- MEMORY USAGE ---"
free -h
echo ""
echo "--- DISK USAGE ---"
df -h | grep -E '^/dev/'
echo ""
echo "--- RUNNING PROCESSES ---"
echo "Total processes: $(ps aux | wc -l)"
echo ""
echo "--- TOP 5 MEMORY-CONSUMING PROCESSES ---"
ps aux --sort=-%mem | head -6
echo ""
echo "========================================"
echo " Health check complete!"
echo "========================================"
What does this script do?
The #!/bin/bash line at the top is called a shebang. It tells Linux to run this file using the Bash interpreter.
The script uses command substitution with $() to run commands and embed their output inside echo statements. For example, $(date) inserts the current date and time into the report header.
- hostname prints the server's name.
- uptime shows how long the server has been running and the current load.
- nproc returns the number of CPU cores.
- free -h shows memory usage in human-readable format.
- df -h | grep -E '^/dev/' shows disk usage, filtered to only physical devices.
- ps aux --sort=-%mem | head -6 lists the top 5 processes by memory usage. It uses head -6 because the first line is the column header.
- Save the file by pressing Ctrl+O, then press Enter to confirm the filename.
- Exit nano by pressing Ctrl+X.
- Make the script executable by running:
chmod +x health-check.sh
Why chmod +x?
By default, new files on Linux are not executable. The chmod +x command adds the execute permission so you can run the script directly with ./health-check.sh instead of calling bash health-check.sh every time.
- Confirm the file exists and has the right permissions by running:
ls -la health-check.sh
You should see -rwxr-xr-x at the start of the line. The x characters confirm the file is executable.
Don't see the file?
- Make sure you're in the ~/project directory. Run pwd to check.
- If you accidentally exited nano without saving, open it again with nano health-check.sh and paste the script in.
- Why isn't my health-check.sh file showing up after saving in nano?
✔️ Awesome, I've got everything!
Great. Double check you've saved your file before moving on.
ⓧ I'd like to double check the full code
Here's the complete health-check.sh script. You can open it in nano to compare:
#!/bin/bash
# health-check.sh - System Health Check Script
# Reports key system vitals for a Linux server
echo "========================================"
echo " SYSTEM HEALTH CHECK REPORT"
echo " Generated: $(date)"
echo "========================================"
echo ""
echo "--- HOSTNAME ---"
hostname
echo ""
echo "--- UPTIME ---"
uptime
echo ""
echo "--- CPU INFO ---"
echo "CPU cores: $(nproc)"
echo "Load averages: $(cat /proc/loadavg | awk '{print $1, $2, $3}')"
echo ""
echo "--- MEMORY USAGE ---"
free -h
echo ""
echo "--- DISK USAGE ---"
df -h | grep -E '^/dev/'
echo ""
echo "--- RUNNING PROCESSES ---"
echo "Total processes: $(ps aux | wc -l)"
echo ""
echo "--- TOP 5 MEMORY-CONSUMING PROCESSES ---"
ps aux --sort=-%mem | head -6
echo ""
echo "========================================"
echo " Health check complete!"
echo "========================================"
Create the log analyzer script
Log files are how Linux records everything that happens on the server. When something breaks, the first place you look is the logs. Manually scrolling through thousands of lines is slow, so you'll write a script that does the searching for you.
- Create a new file called log-analyzer.sh in nano by running:
nano log-analyzer.sh
- Type or paste the following script into nano:
#!/bin/bash
# log-analyzer.sh - Log File Analyzer Script
# Parses a log file and summarizes errors and warnings
if [ -z "$1" ]; then
echo "Usage: $0 <log-file-path>"
echo "Example: $0 /var/log/cloud-init.log"
exit 1
fi
LOG_FILE="$1"
if [ ! -f "$LOG_FILE" ]; then
echo "Error: File '$LOG_FILE' not found!"
exit 1
fi
TOTAL_LINES=$(wc -l < "$LOG_FILE")
ERROR_COUNT=$(grep -ci 'error' "$LOG_FILE")
WARNING_COUNT=$(grep -ci 'warning' "$LOG_FILE")
echo "========================================"
echo " LOG ANALYSIS REPORT"
echo " File: $LOG_FILE"
echo " Generated: $(date)"
echo "========================================"
echo ""
echo "Total lines: $TOTAL_LINES"
echo "Error count: $ERROR_COUNT"
echo "Warning count: $WARNING_COUNT"
echo ""
if [ "$ERROR_COUNT" -gt 0 ]; then
echo "--- 10 MOST RECENT ERRORS ---"
grep -i 'error' "$LOG_FILE" | tail -10
echo ""
fi
if [ "$WARNING_COUNT" -gt 0 ]; then
echo "--- 10 MOST RECENT WARNINGS ---"
grep -i 'warning' "$LOG_FILE" | tail -10
echo ""
fi
echo "========================================"
echo " Analysis complete!"
echo "========================================"
What does this script do?
This script takes a log file path as a command-line argument. The $1 variable holds the first argument the user passes when running the script.
- The first if block checks whether the user provided a file path at all. If not, it prints a usage message and exits.
- The second if block checks whether the file actually exists on disk before trying to read it.
- grep -ci 'error' counts lines containing the word "error" in any case. The -c flag counts matches. The -i flag makes it case-insensitive.
- The conditional sections at the end only print error and warning details if any were found. This keeps the report clean when a log file has no issues.
- Save the file by pressing Ctrl+O, then press Enter to confirm.
- Exit nano by pressing Ctrl+X.
- Make the script executable by running:
chmod +x log-analyzer.sh
- Confirm both scripts are in your project directory by running:
ls -la ~/project/
You should see both health-check.sh and log-analyzer.sh listed with -rwxr-xr-x permissions.
Missing the execute permission?
- If you see -rw-r--r-- instead (no x characters), run chmod +x health-check.sh log-analyzer.sh again.
- My scripts don't have execute permissions even after running chmod +x. What's wrong?
✔️ Awesome, I've got everything!
Both scripts are ready to run. Make sure you've saved both files before moving on.
ⓧ I'd like to double check the full code
Here's the complete log-analyzer.sh script. You can open it in nano to compare:
#!/bin/bash
# log-analyzer.sh - Log File Analyzer Script
# Parses a log file and summarizes errors and warnings
if [ -z "$1" ]; then
echo "Usage: $0 <log-file-path>"
echo "Example: $0 /var/log/cloud-init.log"
exit 1
fi
LOG_FILE="$1"
if [ ! -f "$LOG_FILE" ]; then
echo "Error: File '$LOG_FILE' not found!"
exit 1
fi
TOTAL_LINES=$(wc -l < "$LOG_FILE")
ERROR_COUNT=$(grep -ci 'error' "$LOG_FILE")
WARNING_COUNT=$(grep -ci 'warning' "$LOG_FILE")
echo "========================================"
echo " LOG ANALYSIS REPORT"
echo " File: $LOG_FILE"
echo " Generated: $(date)"
echo "========================================"
echo ""
echo "Total lines: $TOTAL_LINES"
echo "Error count: $ERROR_COUNT"
echo "Warning count: $WARNING_COUNT"
echo ""
if [ "$ERROR_COUNT" -gt 0 ]; then
echo "--- 10 MOST RECENT ERRORS ---"
grep -i 'error' "$LOG_FILE" | tail -10
echo ""
fi
if [ "$WARNING_COUNT" -gt 0 ]; then
echo "--- 10 MOST RECENT WARNINGS ---"
grep -i 'warning' "$LOG_FILE" | tail -10
echo ""
fi
echo "========================================"
echo " Analysis complete!"
echo "========================================"
Run and test your scripts
Both scripts are written and executable. Time to run them against real data on your server and see what they report.
- Run the health check script by running:
./health-check.sh
You should see a formatted report with your server's hostname, uptime, CPU core count, memory usage, disk usage, total process count, and the top 5 memory-consuming processes.
What should I see?
The output starts with a header showing the current date and time. Each section is separated by labels like --- HOSTNAME --- and --- MEMORY USAGE ---.
Your t2.micro instance has 1 CPU core and 1 GiB of RAM, so the memory and CPU values will be small. That is expected.
Seeing 'Permission denied'?
- Run chmod +x health-check.sh and try again. This error means the file is missing execute permissions.
- Make sure you're running the script from the ~/project directory where the file lives.
- Why am I getting 'Permission denied' when running my Bash script on EC2?
Now test the log analyzer against a real log file on your server.
- Run the log analyzer against the cloud-init log by running:
./log-analyzer.sh /var/log/cloud-init.log
You should see a report showing the total number of lines in the log, plus counts and recent entries for any errors and warnings found.
What is cloud-init?
The /var/log/cloud-init.log file records everything that happened when your EC2 instance first booted up. It is a great test file because it is always present on an EC2 instance and typically contains a mix of normal, warning, and error entries.
- Try running the script without any arguments to verify the input validation works:
./log-analyzer.sh
You should see the usage message: Usage: ./log-analyzer.sh <log-file-path>. This confirms your input validation is working correctly.
- Test it with a file that does not exist to verify the second validation:
./log-analyzer.sh /var/log/fake.log
You should see Error: File '/var/log/fake.log' not found!. Both safety checks are working.
Seeing unexpected output from the log analyzer?
- If you see 0 for all counts, the log file might be empty or very short. Try a different log file like /var/log/messages.
- If you see a syntax error, open the script with nano log-analyzer.sh and check that every if block has a matching fi at the end.
- My log-analyzer.sh script shows a syntax error or unexpected results. Can you help me debug?
You now have two working automation scripts on your Linux server. Next up, you'll cleanly shut everything down and make sure no resources are left running in your AWS account.
Clean Up Your AWS Resources
Your Bash scripts are complete. You have a health checker that reports system vitals and a log analyzer that surfaces errors from any log file.
Now it's time to clean up. Terminating your EC2 instance and deleting the key pair ensures you stay within the AWS Free Tier and don't accumulate unexpected charges.
In this step, get ready to:
- Terminate the EC2 instance from local PowerShell.
- Delete the SSH key pair and verify no resources remain.
Terminate your EC2 instance
You need to run the termination command from your local PowerShell, not from inside the SSH session. The first thing to do is exit your SSH connection and return to your local terminal.
- Exit your SSH session on the EC2 instance by running:
exit
You should now see your local PowerShell prompt instead of the ec2-user@ Linux prompt.
To terminate your instance, you need its instance ID. This is the unique identifier AWS assigned when you launched it in Step 1.
- Look up the instance ID of your my-linux-server instance by running:
aws ec2 describe-instances --filters "Name=tag:Name,Values=my-linux-server" "Name=instance-state-name,Values=running" --query "Reservations[].Instances[].InstanceId" --output text
What does this command do?
The --filters flag narrows the search to instances tagged my-linux-server that are currently in the running state. The --query flag extracts just the instance ID from the response so it's easy to copy.
The output is a single value like i-0abc123def456789. Copy this ID for the next command.
- Terminate the instance by running:
aws ec2 terminate-instances --instance-ids [[INSTANCE_ID="your instance ID e.g. i-0abc123def456789"]]
What does this command do?
This tells AWS to shut down and permanently delete your EC2 instance. The attached EBS storage volume is also deleted automatically.
The command responds immediately with a state change showing running changing to shutting-down. The instance takes about 30 seconds to fully reach the terminated state.
- Wait about 30 seconds, then verify the instance is terminated by running:
aws ec2 describe-instances --instance-ids [[INSTANCE_ID="your instance ID e.g. i-0abc123def456789"]] --query "Reservations[].Instances[].State.Name" --output text
- You should see terminated in the output.
Still seeing shutting-down?
The instance can take up to a minute to fully terminate. Wait another 30 seconds and run the describe-instances command again.
If the state is stuck on running and not changing at all, double check that you pasted the correct instance ID. Why isn't my EC2 instance terminating?
Delete the key pair and verify cleanup
The SSH key pair you created for connecting to the instance is still stored in AWS. Now that the instance is gone, the key pair has no use.
- Delete the key pair from AWS by running:
aws ec2 delete-key-pair --key-name my-ec2-key
What does this command do?
This removes the public key that AWS stored when you created the my-ec2-key pair in Step 1. The command does not produce any output on success.
You can also remove the local .pem file from your computer. This is optional since the key no longer connects to anything.
- If you want to remove the local private key file, run:
Remove-Item .\my-ec2-key.pem
Keep or delete the .pem file?
Since you already deleted the key pair from AWS, this .pem file can't be used to connect to any instance. Deleting it keeps your filesystem tidy. If you prefer to keep it as a reference, that's fine too.
As a final check, confirm that no running instances remain in your AWS account.
- List all non-terminated instances by running:
aws ec2 describe-instances --filters "Name=instance-state-name,Values=running,stopped" --query "Reservations[].Instances[].{ID:InstanceId,State:State.Name,Name:Tags[?Key=='Name'].Value|[0]}" --output table
What should I see?
If the cleanup was successful, you should see an empty table or no output at all. This means you have no running or stopped instances in your account.
- You can also verify in the AWS Console by opening the EC2 dashboard in your browser.
- Under Resources, confirm that Running instances shows 0.
Still seeing a running instance?
Check the instance's Name tag in the EC2 dashboard. If it is not my-linux-server, it belongs to a different project or was created outside this tutorial.
If you see my-linux-server still in the running state, re-run the terminate-instances command with the correct instance ID. Help me clean up my EC2 resources.
Secret mission
Automate Backups to S3
Your scripts are sitting on a single EC2 instance with no safety net. If that instance crashes or gets terminated, your work vanishes. In this mission, you will write a Bash script that compresses files into a timestamped archive and uploads them to Amazon S3.
Before you go
Clean Up Your Resources
This project used AWS cloud resources that are covered by the Free Tier. Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
Cost warning
Your EC2 t2.micro instance is covered under the AWS Free Tier (750 hours/month for 12 months from account creation). If your Free Tier has expired or you run multiple instances, you will be charged for usage.
If you completed Step 5, your instance is already terminated and there is nothing left to pay for. If you skipped Step 5, choose Pause or Delete below to avoid unexpected charges.
Resources you used:
- 1x EC2 t2.micro instance (Free Tier: 750 hours/month for 12 months).
- 1x EBS gp2 volume attached to the instance (Free Tier: 30 GB/month for 12 months).
- 1x SSH key pair stored in AWS and a local .pem file on your Desktop.
- 1x S3 bucket (Secret Mission only, Free Tier: 5 GB storage).
Keep everything running
No action needed. Choose this if you want to keep practicing Linux commands on your instance.
- Your instance continues running and counts against your 750 Free Tier hours per month.
- You can reconnect anytime using the same ssh -i command from Step 1.
- Keep an eye on your Free Tier usage in the AWS Billing dashboard to avoid surprise charges.
Pause - I'll come back to this later
Stop the instance to pause compute charges while keeping your files and scripts intact. You can restart it later and pick up where you left off.
- Stop your EC2 instance by running this command in your local PowerShell:
aws ec2 stop-instances --instance-ids [[INSTANCE_ID="your instance ID from Step 1"]]
- The instance state changes to stopped. You stop paying for compute hours while it is stopped.
- The EBS volume remains attached and still counts toward the 30 GB Free Tier storage limit.
- To restart later, run aws ec2 start-instances --instance-ids with your instance ID. Note that the public IP address will change after restarting.
Delete - I don't want to use this again
Remove all project resources so nothing remains in your AWS account. If you already completed Step 5, your instance and key pair are already deleted.
- If you have not completed Step 5, terminate your EC2 instance by running this command in your local PowerShell:
aws ec2 terminate-instances --instance-ids [[INSTANCE_ID="your instance ID from Step 1"]]
- Delete the key pair from AWS:
aws ec2 delete-key-pair --key-name [[KEY_NAME="your key pair name"]]
- Delete the local .pem file from your Desktop.
- If you completed the Secret Mission, delete your S3 bucket and its contents:
aws s3 rb s3://[[BUCKET_NAME="your-bucket-name"]] --force
- Verify everything is cleaned up by checking the EC2 dashboard in the AWS Console. Your instance should show a terminated state.
Mission accomplished
That's a Wrap!
You just launched a Linux server on AWS, learned to administer it from the command line, and wrote real automation scripts. That is the day-one toolkit for any cloud engineer.
Here's what you accomplished:
- Launched an Amazon Linux 2023 EC2 instance entirely from the AWS CLI, connected via SSH from Windows PowerShell, and navigated the Linux filesystem to manage files, permissions, users, and processes.
- Wrote a health-check.sh Bash script that reports system vitals like CPU load, memory usage, disk usage, and running processes.
- Wrote a log-analyzer.sh script that parses log files, counts errors and warnings, and surfaces the most recent entries.
- Secret Mission: Built a backup-to-s3.sh script that archives a directory into a timestamped tarball and uploads it to an S3 bucket using the AWS CLI.
Ready to quiz yourself?