Provision an EC2 Instance with the CLI
Launch, secure, and connect to an EC2 web server entirely from your terminal.
Introduction
30 Second Summary
Every cloud server you've ever visited started as someone turning an idea into a running machine. Most people click through web consoles to do this, but professionals type a single command and watch infrastructure appear in seconds.
In this project, you will provision a live EC2 instance entirely from your terminal using the AWS CLI. You'll create security credentials, launch a server, deploy a web page, and learn how network rules control who can reach it.
What You'll Build
You'll type a URL into your browser and see a web page served from a cloud server that you launched, secured, and connected to without ever opening the AWS console.
By the end of this project, you'll have:
- A running EC2 instance launched with a single CLI command, visible transitioning from pending to running.
- A live Nginx web server accessible from your browser, proving your instance is serving traffic to the internet.
- First-hand experience with a security group blocking HTTP traffic and then fixing it in real time from the command line.
- Secret Mission: Automate the entire web server setup with a user data script so the instance serves pages the moment it boots.
Are there any prerequisites?
You'll need an existing AWS account with IAM credentials (an access key ID and secret access key) already created. You'll also need a Mac with Terminal available.
Before We Start
Before we dive in, let's lock in what you're about to build and why it matters.
Install and Configure the AWS CLI
To provision an EC2 instance from the command line, your terminal needs a way to talk to AWS. The AWS CLI is the tool that makes this possible.
In this step, you'll install the AWS CLI v2, connect it to your AWS account using your IAM credentials, and verify that everything is authenticated.
In this step, get ready to:
- Install the AWS CLI v2 on macOS.
- Configure the CLI with your IAM credentials.
- Confirm authentication works with a test command.
Install the AWS CLI v2
The AWS CLI v2 lets you run commands in your terminal that create, manage, and delete AWS resources. Before installing, let's check whether you already have it.
- Press Cmd+Space to open Spotlight.
- Type Terminal and press Enter to open it.
- Check if the AWS CLI is already installed by running:
aws --version
✔️ I see aws-cli/2.
You already have AWS CLI v2 installed. Skip ahead to the next substep to configure your credentials.
ⓧ I see aws-cli/1.
You have an older version of the AWS CLI. Install v2 by running these commands:
curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
sudo installer -pkg AWSCLIV2.pkg -target /
What do these commands do?
- The first command downloads the official AWS CLI v2 installer package to your current directory.
- The second command runs the installer. Your Mac will ask for your admin password because sudo runs the command with elevated privileges.
- After installation completes, run aws --version again.
- You should now see output starting with aws-cli/2. confirming v2 is installed.
ⓧ Command not found
The AWS CLI isn't installed yet. Install it by running these commands:
curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
sudo installer -pkg AWSCLIV2.pkg -target /
What do these commands do?
- The first command downloads the official AWS CLI v2 installer package to your current directory.
- The second command runs the installer. Your Mac will ask for your admin password because sudo runs the command with elevated privileges.
- After installation completes, confirm it worked by running:
aws --version
- You should see output starting with aws-cli/2. confirming the installation succeeded.
Still seeing command not found?
- Close your Terminal window and open a new one. The installer updates your PATH, but existing Terminal sessions may not pick up the change.
- If it still fails, check that macOS is version 11 or later. AWS CLI v2 requires macOS 11+.
help me troubleshoot my AWS CLI installation on macOS
Configure the CLI with your IAM credentials
Now that the CLI is installed, you need to tell it who you are. The aws configure command stores your IAM credentials locally so every future command authenticates automatically.
What will aws configure ask?
The command is interactive. It will prompt you for four values one at a time. Have your IAM access key ID and secret access key ready before you run it.
- Access Key ID — paste your IAM access key ID.
- Secret Access Key — paste your IAM secret access key.
- Default region name — type us-east-1 (this is the region your resources will launch in).
- Default output format — type json (this makes CLI responses easy to read).
- Start the configuration by running:
aws configure
- Paste your access key ID when prompted and press Enter.
- Paste your secret access key when prompted and press Enter.
- Type us-east-1 for the default region and press Enter.
- Type json for the output format and press Enter.
The command finishes silently. No output means it saved your credentials successfully.
Seeing an error about invalid credentials?
- Double-check that you copied the full access key ID and secret access key from your IAM console without any extra spaces.
- If you need to re-enter them, just run aws configure again. It overwrites the previous values.
help me fix my AWS CLI credentials configuration
Confirm authentication
Configuration is done, but you haven't proven the credentials actually work yet. The aws sts get-caller-identity command asks AWS to confirm who you are.
Before you run this, what do you think the command will return if your credentials are valid?
- Test your authentication by running:
aws sts get-caller-identity
You should see a JSON response with three fields: your Account number, UserId, and Arn.
What does this output mean?
- Account is your 12-digit AWS account number.
- UserId is the unique identifier for the IAM user whose credentials you configured.
- Arn is the Amazon Resource Name, a full path to your IAM identity.
If you see all three fields, your CLI is authenticated and ready to provision resources.
Seeing an error instead of JSON?
- An "InvalidClientTokenId" error means your access key ID is wrong. Re-run aws configure and paste it again carefully.
- A "SignatureDoesNotMatch" error means your secret access key is wrong. Re-run aws configure and paste the secret key again.
- If you can't find your secret access key, you may need to create a new access key in the IAM console. The secret key is only shown once at creation time.
help me fix my AWS CLI authentication error
Your CLI is installed, configured, and talking to AWS. Next up, you'll create the key pair and security group that your EC2 instance needs before it can launch.
Create a Key Pair and Security Group
Your CLI is connected to AWS and ready to go. Now it's time to create the foundational resources your EC2 instance needs before it can launch.
Every EC2 instance needs two things: an SSH key pair for secure login, and a security group that acts as a firewall controlling what traffic can reach it. Without these, your instance would be either inaccessible or wide open to the internet.
In this step, get ready to:
- Create an SSH key pair for logging into your instance.
- Create a security group with SSH-only access.
- Verify both resources exist in AWS.
Create an SSH key pair and save the private key
A key pair is a set of cryptographic keys. AWS holds the public key, and you hold the private key locally. When you connect via SSH, your private key proves your identity to the server.
- Create your key pair and save the private key to a local file by running this command:
aws ec2 create-key-pair --key-name my-ec2-key --key-type ed25519 --query "KeyMaterial" --output text > my-ec2-key.pem
What does this command do?
- --key-name my-ec2-key names your key pair inside AWS.
- --key-type ed25519 uses a modern, secure key algorithm that's smaller and faster than RSA.
- --query "KeyMaterial" --output text extracts just the private key content from the response.
- > my-ec2-key.pem redirects the private key into a local file.
- Lock down the file permissions so SSH will accept it by running:
chmod 400 my-ec2-key.pem
Why chmod 400?
SSH refuses to use a private key file that other users on the system can read. chmod 400 makes the file read-only for you and invisible to everyone else. Without this, SSH will reject the key with a "permissions too open" error.
- Confirm the key pair exists in AWS by running:
aws ec2 describe-key-pairs --key-names my-ec2-key
You should see a JSON response containing your key's KeyPairId, KeyName, and KeyFingerprint. This confirms AWS has your public key stored.
Seeing 'InvalidKeyPair.NotFound'?
- Make sure you typed the key name exactly as my-ec2-key with hyphens, not underscores.
- Confirm your CLI region is us-east-1 by running aws configure get region. Key pairs are region-specific.
- help me troubleshoot my key pair creation
Create a security group with SSH-only access
A security group is a virtual firewall for your instance. You define inbound rules about which traffic is allowed to reach it. Right now, you only need SSH access on port 22.
- Create the security group by running:
aws ec2 create-security-group --group-name my-ec2-sg --description "SSH access only"
What should I see?
The command returns a JSON object with a GroupId field (something like sg-0abc1234def56789). This is your security group's unique identifier.
Note this GroupId somewhere. You may need it in later steps.
- Add an inbound rule that allows SSH connections by running:
aws ec2 authorize-security-group-ingress --group-name my-ec2-sg --protocol tcp --port 22 --cidr 0.0.0.0/0
What does this rule allow?
- --protocol tcp --port 22 allows TCP traffic on port 22, which is the SSH port.
- --cidr 0.0.0.0/0 means "from any IP address." This is fine for a short-lived learning project. For production servers, you would restrict this to your own IP address.
Notice you are NOT adding a rule for port 80 (HTTP). This is intentional. You will see why this matters when you try to reach a web server in a later step.
Before you run the next command, what do you expect to see if both resources were created successfully?
- Verify your security group and its rules by running:
aws ec2 describe-security-groups --group-names my-ec2-sg
You should see a JSON response showing your security group with its GroupId, GroupName, and an IpPermissions array containing your port 22 TCP rule.
Seeing 'InvalidGroup.NotFound'?
- Check you typed the group name exactly as my-ec2-sg with hyphens.
- Security groups are region-specific. Confirm your region with aws configure get region.
- If the authorize-security-group-ingress command failed, the security group still exists but has no inbound rules. Re-run the authorize command.
- help me troubleshoot my security group
Your key pair and security group are locked and loaded. Next up, you will launch a real EC2 instance and SSH into it to prove it's yours.
Launch the Instance and SSH In
Your security foundation is in place. You have an SSH key to prove your identity and a security group to control who gets through the door.
Now for the main event. You'll launch a real server in the cloud with a single CLI command, then connect to it over SSH to prove it's yours.
In this step, get ready to:
- Retrieve the latest Amazon Linux AMI ID from AWS.
- Launch a t3.micro EC2 instance with your key pair and security group.
- SSH into the running instance and see the welcome banner.
Retrieve the latest Amazon Linux AMI ID
AWS updates their Amazon Linux images regularly. Instead of hardcoding an AMI ID that could go stale, you'll query SSM Parameter Store for the latest one.
- Retrieve the latest Amazon Linux 2023 AMI ID by running this command:
aws ssm get-parameters \
--names /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \
--query "Parameters[0].Value" \
--output text
What does this command do?
- /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 is a public SSM parameter that always points to the latest Amazon Linux 2023 AMI for x86_64.
- --query "Parameters[0].Value" extracts just the AMI ID string from the response.
- --output text gives you a plain string you can copy directly into the next command.
- You should see an AMI ID printed to the terminal, something like ami-0abcdef1234567890. Copy this value for the next command.
Don't see an AMI ID?
- Make sure your CLI is configured for us-east-1. Run aws configure get region to check.
- If you see an access denied error, your IAM user may not have SSM read permissions. Check that your user has the AmazonSSMReadOnlyAccess policy or broader admin access.
- Help me debug my SSM parameter query
Launch a t3.micro instance
With your AMI ID in hand, you can launch an instance. This single command combines everything: the image, instance size, SSH key, and security group.
- Launch your EC2 instance by running this command:
aws ec2 run-instances \
--image-id [[AMI_ID="your AMI ID from the previous command"]] \
--instance-type t3.micro \
--key-name my-ec2-key \
--security-groups my-ec2-sg \
--count 1 \
--tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=my-web-server}]"
What does this command do?
- --image-id tells AWS which operating system image to install on the instance.
- --instance-type t3.micro sets the server size. This is free tier eligible.
- --key-name my-ec2-key attaches your SSH key so you can log in remotely.
- --security-groups my-ec2-sg applies your firewall rules to control network access.
- --tag-specifications gives your instance the human-readable name my-web-server in the AWS console.
- You'll see a large JSON response. Find the InstanceId field near the top. It starts with i-. Copy this value.
- Wait for the instance to reach the running state by running:
aws ec2 wait instance-running \
--instance-ids [[INSTANCE_ID="your instance ID starting with i-"]]
What does this command do?
This command polls AWS every 15 seconds, checking whether your instance has transitioned from pending to running. It exits silently when the instance is ready.
The command returns with no output when the instance is running. No news is good news here.
Seeing an error on the run-instances command?
- If you see InvalidAMIID.NotFound, double-check that you copied the full AMI ID from the previous step (it should start with ami-).
- If you see InvalidGroup.NotFound, confirm your security group exists by running aws ec2 describe-security-groups --group-names my-ec2-sg.
- Help me fix my run-instances error
Get the public IP and SSH in
Your instance is running, but you need its public IP address to connect from your local machine.
- Get your instance's public IP address by running:
aws ec2 describe-instances \
--instance-ids [[INSTANCE_ID="your instance ID starting with i-"]] \
--query "Reservations[0].Instances[0].PublicIpAddress" \
--output text
- You should see an IP address printed to the terminal, like 3.95.174.82. Copy this value.
Before you run the next command, what do you expect to see when you connect to a brand new Amazon Linux server for the first time?
- Connect to your instance over SSH by running:
ssh -i my-ec2-key.pem ec2-user@[[PUBLIC_IP="your instance public IP address"]]
What happens when you connect?
The first time you SSH into a new server, your terminal asks you to confirm the server's fingerprint. Type yes and press Enter to continue.
After that, you'll see the Amazon Linux welcome banner and a command prompt ending in $. You're now logged into your cloud server.
Can't connect via SSH?
- Make sure you're running the SSH command from the same directory where my-ec2-key.pem is saved.
- Check that the key file permissions are still 400. Run ls -la my-ec2-key.pem to verify.
- If you see a connection timeout, wait 30 seconds and try again. The instance may still be initializing its network interface.
- Help me troubleshoot my SSH connection
You're connected to a live server in the cloud. Next up, you'll install a web server and discover what happens when your security group blocks the traffic.
Deploy Nginx and Hit the Wall
Your instance is up and you're connected via SSH. Now it's time to turn this bare server into something visible by installing a web server that can serve pages to anyone who visits your public IP.
Nginx is one of the most popular web servers in the world. You'll install it on your instance and visit your server's public IP in a browser to see it in action.
In this step, get ready to:
- Install and start Nginx on your EC2 instance.
- Test whether your web server is reachable from the internet.
Install and start Nginx on the instance
You're already inside your EC2 instance via SSH. From here, you can install software just like you would on any Linux machine.
- Install Nginx by running this command:
sudo dnf install nginx -y
What does this command do?
Amazon Linux 2023 uses dnf as its package manager. The -y flag automatically confirms the installation so you don't have to type "yes" at a prompt.
- Start Nginx and enable it to run on boot by running these commands:
sudo systemctl start nginx
sudo systemctl enable nginx
What do these commands do?
The start command launches Nginx right now. The enable command ensures Nginx starts automatically if the instance ever reboots.
Before you test this from the outside, what do you think you'd see if you made a web request from inside the server itself?
- Test that Nginx is running locally by running:
curl localhost
You should see a block of HTML output in your terminal. This is the default Nginx welcome page being served locally on the instance.
Seeing "command not found" or no HTML output?
Make sure you typed sudo dnf install nginx -y with the -y flag. If the install failed, check that you're connected to the internet on the instance by running ping google.com and pressing Ctrl+C to stop it.
If Nginx installed but curl localhost returns nothing, confirm the service is running with sudo systemctl status nginx. You should see "active (running)" in the output.
help me troubleshoot why curl localhost isn't returning the Nginx welcome page on my Amazon Linux 2023 EC2 instance
Access the web server from your browser
Nginx is confirmed working locally. Now let's see if the rest of the world can reach it too.
Before you open your browser, take a moment to predict: what do you think will happen when you visit your server's public IP over HTTP?
- Open a new tab in your browser and paste this URL:
http://[[EC2_PUBLIC_IP="your instance's public IP address"]]
The page does not load. Your browser spins and eventually times out with a "connection timed out" or "site can't be reached" message.
This is not a bug. This is your security group doing exactly what you told it to do.
Why can't I reach my web server?
When you created my-ec2-sg in Step 2, you only opened port 22 for SSH traffic. Port 80, which is the port browsers use for HTTP requests, is completely blocked.
Nginx is happily serving pages inside the instance. But the security group acts as a firewall that sits in front of your instance and drops any traffic you haven't explicitly allowed. No port 80 rule means no HTTP traffic gets through.
- Return to your terminal and exit the SSH session by typing:
exit
You should see your local terminal prompt return. You're no longer on the remote instance.
Page loaded instead of timing out?
If you see the Nginx welcome page, your security group may have a rule allowing port 80 that was added accidentally. Run aws ec2 describe-security-groups --group-names my-ec2-sg from your local terminal and check whether an HTTP rule (port 80) appears in the output.
help me check why my security group is allowing HTTP traffic when I only intended to open SSH
You just saw a security group act as a real firewall. Nginx is running, curl localhost proves it works from inside, but the outside world can't get through port 80. In the next step, you'll add a single rule to fix this and watch your web page come alive.
Open HTTP Traffic and Verify
In the last step, you proved that your security group is a real firewall. Your Nginx web server is running and responding locally, but the outside world can't reach it because port 80 is blocked.
The fix is a single CLI command. You'll add an HTTP ingress rule to your security group and then confirm your web page loads in the browser.
In this step, get ready to:
- Add an HTTP ingress rule to your security group.
- Verify your web server is publicly accessible in the browser.
Add an HTTP ingress rule to your security group
Your security group my-ec2-sg currently allows only SSH traffic on port 22. You need to open port 80 so HTTP requests from the internet can reach Nginx.
- Add an inbound rule for HTTP traffic by running this command:
aws ec2 authorize-security-group-ingress --group-name my-ec2-sg --protocol tcp --port 80 --cidr 0.0.0.0/0
What does this command do?
This command adds a new inbound rule to your security group that permits TCP traffic on port 80 from any IP address.
Port 80 is the default port for HTTP web traffic. The --cidr 0.0.0.0/0 part means "from anywhere on the internet", which is what you want for a public web server.
- Confirm both rules are now active by running:
aws ec2 describe-security-groups --group-names my-ec2-sg --query "SecurityGroups[0].IpPermissions"
You should see two entries in the JSON output. One for port 22 (SSH) and one for port 80 (HTTP).
Not seeing two rules?
- If you see an error mentioning a duplicate permission, the rule already exists. Run the describe command above to confirm both entries are present.
- If you see a "not found" error, double-check that your group name matches exactly: my-ec2-sg.
- Help me troubleshoot my security group ingress rule.
Verify your web server is publicly accessible
The security group update takes effect immediately. Your EC2 instance's Nginx server should now be reachable from the internet.
Before you refresh the page, what do you expect to see this time? The only change you made was adding port 80 to the security group.
- Return to your browser and go to http:// followed by your instance's public IP address.
The Nginx welcome page loads successfully. Your EC2 instance is publicly serving web traffic.
Why did one command fix everything?
Security groups are stateful firewalls. When you add an inbound rule, AWS automatically allows the corresponding outbound response traffic.
That single rule addition was the only barrier between your running web server and the outside world. Nothing else changed about Nginx or the instance itself.
A note on 0.0.0.0/0 in production
Opening SSH (port 22) to 0.0.0.0/0 means anyone on the internet can attempt to connect. For this learning project, that's fine because the key pair protects access.
In a real deployment, you'd restrict SSH to your own IP address and leave HTTP open to the world.
Page still not loading?
- Make sure you're using http:// and not https://. Nginx is not configured for HTTPS in this project.
- Verify you're using the correct public IP address from Step 3. Some browsers auto-complete URLs, which can send you to the wrong address.
- Try a hard refresh with Cmd+Shift+R (macOS) or Ctrl+Shift+R (Windows) to bypass any cached timeout.
- Help me figure out why my Nginx page won't load.
Secret mission
Automate with User Data
You manually SSH'd into your instance and installed Nginx by hand. In production, nobody does that. Can you relaunch a fresh instance that sets itself up automatically the moment it boots?
Clean Up Your Resources
Clean Up Your Resources
This project uses a t3.micro EC2 instance that counts against your AWS Free Tier hours. If you completed the Secret Mission, you already terminated and cleaned up all resources. If you stopped after Step 5, decide whether to keep your instance running or delete everything.
Cost warning
A running t3.micro instance counts against your Free Tier allowance (750 hours/month for pre-July 2025 accounts, credit-based for newer accounts). If you exceed your Free Tier limits, you will be billed at on-demand rates.
If you are not actively using the instance, terminate it now to avoid unexpected charges.
Resources you used:
- EC2 instance (t3.micro) tagged my-web-server.
- Key pair my-ec2-key (AWS resource and local my-ec2-key.pem file).
- Security group my-ec2-sg.
- Local file user-data.sh.
- Downloaded installer AWSCLIV2.pkg.
Keep everything running
No action needed. Choose this if you are still actively experimenting with your EC2 instance or plan to continue building on it today.
- Your instance continues to run and count against your Free Tier hours.
- The AWS CLI remains installed on your Mac for future AWS projects.
- Your local my-ec2-key.pem and user-data.sh files remain on your Desktop.
Pause - I'll come back to this later
Stop the instance to avoid ongoing charges while keeping your resources intact. You can restart it later without recreating anything.
- Stop your instance by running:
aws ec2 stop-instances --instance-ids [[INSTANCE_ID="your instance ID"]]
- A stopped instance incurs no compute charges, but its attached EBS volume still uses a small amount of Free Tier storage.
- To restart later, run:
aws ec2 start-instances --instance-ids [[INSTANCE_ID="your instance ID"]]
- The public IP address changes each time you stop and start the instance. Run the describe-instances command again to get the new IP.
Delete - I don't want to use this again
Remove all project resources and start fresh. If you already cleaned up during the Secret Mission, you can skip this tab. Run each command in order.
- Terminate your EC2 instance by running:
aws ec2 terminate-instances --instance-ids [[INSTANCE_ID="your instance ID"]]
- Wait for the instance to fully terminate by running:
aws ec2 wait instance-terminated --instance-ids [[INSTANCE_ID="your instance ID"]]
- Delete the security group by running:
aws ec2 delete-security-group --group-name my-ec2-sg
- Delete the key pair from AWS by running:
aws ec2 delete-key-pair --key-name my-ec2-key
- Remove the local private key file, user data script, and installer package by running:
rm my-ec2-key.pem
rm user-data.sh
rm AWSCLIV2.pkg
What about the AWS CLI itself?
The AWS CLI stays installed on your Mac. It takes minimal disk space and is useful for future AWS projects. If you want to remove it later, refer to the official AWS CLI uninstall documentation for macOS.
Nice Work!
Nice Work!
You just provisioned, secured, connected to, and tore down a live cloud server entirely from your terminal. That's real infrastructure management, no console clicking required.
Here's what you learned:
- Launched an EC2 instance with a single CLI command, created a key pair for SSH access, and connected to your running server in the cloud.
- Experienced a security group blocking HTTP traffic first-hand, then fixed it by adding an ingress rule to open port 80.
- Deployed Nginx on your instance and verified it was publicly accessible from your browser.
- Secret Mission: Automated the entire web server provisioning with a user data script, eliminating manual SSH setup entirely.
Ready to quiz yourself?