Build a Secure Multi-Tier VPC

Design a VPC with public/private subnets, bastion host, and IAM roles.

Introduction

30 Second Summary

Every time you check your bank balance or order food online, your request travels through layers of network security designed to keep bad actors out and sensitive data safe. The same principles that protect those services are exactly what AWS tests on the SAA-C03 exam.

In this project, you will build a secure multi-tier VPC architecture from scratch. You will set up public and private subnets, configure layered security controls, and connect to a private server through a bastion host.

What You'll Build

A production-style VPC with public and private subnets, layered security using security groups and NACLs, a bastion host access pattern, and IAM role-based access to S3.

By the end of this project, you'll have:

  • A custom VPC with public and private subnets, an Internet Gateway, a NAT Gateway, and properly configured route tables.
  • A working bastion host architecture where a private EC2 instance accesses S3 via an IAM role, reachable only through the bastion in the public subnet.
  • Hands-on experience with ENIs and placement groups, plus exam-ready knowledge of stateful vs stateless security and rule evaluation order.
  • Secret Mission: Simulate an ENI failover scenario and verify enhanced networking on your instances.

Are there any prerequisites?

This project assumes you have an AWS account with console access and basic familiarity with launching EC2 instances. You should also have an SSH client available on your Windows machine (OpenSSH is built into Windows 10 and later).

Create Your Custom VPC with Public and Private Subnets

Every production AWS environment starts with network isolation. A VPC (Virtual Private Cloud) gives you a private, logically isolated section of the AWS cloud where you control IP addressing, subnets, routing, and gateways.

In this step, you will build a custom VPC from scratch with a public subnet and a private subnet. The public subnet connects directly to the internet through an Internet Gateway, while the private subnet uses a NAT Gateway for outbound-only internet access.

In this step, get ready to:
  • Create a custom VPC with public and private subnets using the VPC Console wizard.
  • Verify route tables, Internet Gateway, and NAT Gateway are configured correctly.
  • Review SAA-C03 exam scenarios for VPC and subnet design.
Create the VPC and subnets using the VPC Console wizard

AWS provides a guided wizard that creates your VPC, subnets, route tables, and gateways all in one step. This saves you from configuring each resource individually.

  • In the AWS Management Console, type VPC into the search bar at the top of the page.
  • Select VPC from the search results to open the VPC Dashboard.
  • Click Create VPC.
  • Select VPC and more at the top of the page.

Why VPC and more?

The VPC and more option creates your VPC along with subnets, route tables, an Internet Gateway, and optionally a NAT Gateway in one step. It also shows a live preview diagram of your architecture as you configure it.

Now configure the wizard with these settings:

  • In the Name tag auto-generation field, enter exam-lab.
  • For IPv4 CIDR block, enter 10.0.0.0/16.
  • Set Number of Availability Zones to 1.
  • Set Number of public subnets to 1.
  • Set Number of private subnets to 1.
  • For NAT gateways, select In 1 AZ.
  • For VPC endpoints, select None.

What is a CIDR block?

A CIDR block defines the range of IP addresses available in your VPC. 10.0.0.0/16 gives you 65,536 IP addresses (10.0.0.0 through 10.0.255.255). Your public subnet (10.0.1.0/24) and private subnet (10.0.2.0/24) each get 256 addresses from this larger block.

AWS reserves the first four IP addresses and the last IP address in each subnet CIDR block, so you actually get 251 usable addresses per subnet.

  • Verify the preview diagram on the right shows your VPC with one public subnet, one private subnet, route tables, an Internet Gateway, and a NAT Gateway.
  • Click Create VPC.

AWS will now provision all your VPC resources. Wait until you see a green success banner and all resources show a green checkmark.

What did the wizard create?

The wizard created seven resources in one go: a VPC, a public subnet, a private subnet, two route tables (one for each subnet), an Internet Gateway, and a NAT Gateway with an Elastic IP.

All resources are auto-named with the exam-lab prefix you provided. For example, your VPC is named exam-lab-vpc and your public subnet is exam-lab-subnet-public1.

Cost warning

Your NAT Gateway costs $0.045 per hour plus $0.045 per GB of data processed. This is the primary cost driver in this project. Complete the project in one sitting and delete the NAT Gateway when you are done (instructions in Before You Go) to keep costs under $1.

VPC creation failed?

If you see an error during creation, check that your account does not already have 5 VPCs in this region (the default limit). You can delete an unused default or custom VPC to free up space.

Also confirm you selected the correct number of Availability Zones (1) and NAT Gateways (In 1 AZ).

help me troubleshoot my VPC creation error

Verify the route tables and understand the architecture

The wizard created route tables automatically, but you need to confirm they are wired correctly. A misconfigured route table is the most common cause of connectivity issues in a VPC.

  • Click View VPC to go to your new VPC's details page.
  • In the left navigation panel, click Route tables.
  • Find the route table named exam-lab-rtb-public and click its ID to open it.
  • Click the Routes tab.

You should see two routes. The local route (10.0.0.0/16 targeting local) keeps traffic within the VPC. The 0.0.0.0/0 route points to the Internet Gateway (igw-...), sending all other traffic to the internet.

  • Go back to Route tables in the left navigation.
  • Find the route table named exam-lab-rtb-private1 and click its ID to open it.
  • Click the Routes tab.

This route table also has two routes. The local route is the same, but the 0.0.0.0/0 route points to the NAT Gateway (nat-...). This means the private subnet can reach the internet for outbound requests (like downloading updates), but nothing from the internet can initiate a connection inward.

  • Click Subnet associations tab on the private route table.
  • Confirm that exam-lab-subnet-private1 is listed under Explicit subnet associations.
  • In the left navigation, click Internet gateways.
  • Confirm exam-lab-igw shows a State of Attached and is attached to your exam-lab-vpc.

What makes a subnet public or private?

A public subnet has a route to an Internet Gateway. Resources in a public subnet can access the public internet directly. A private subnet has no direct route to an Internet Gateway. Resources in a private subnet require a NAT device to access the public internet.

The subnet itself does not have a "public" or "private" setting. It is the route table association that determines whether a subnet is public or private.

Missing the 0.0.0.0/0 route?

If your public route table does not show a route to an Internet Gateway, or your private route table does not show a route to a NAT Gateway, you may have opened the wrong route table. Check the route table name matches exam-lab-rtb-public or exam-lab-rtb-private1 respectively.

help me find the correct route table for my VPC subnets

Exam Corner: VPC and subnet design

VPC design is a core topic in SAA-C03 Domain 1 (Design Secure Architectures, 30% of the exam). Let's review how the architecture you just built maps to common exam scenarios.

Exam scenario: Public and private subnet separation

"A company needs to deploy web servers accessible from the internet and database servers that should not be directly reachable from the internet. How should they design their VPC?"

Answer: Use public subnets (with an IGW route) for web servers and private subnets (with a NAT Gateway route for outbound-only internet access) for databases. This is exactly the architecture you just built.

Key exam facts for VPC and subnets

  • A public subnet has a route to an Internet Gateway. A private subnet has no direct IGW route.
  • NAT Gateways allow outbound internet access from private subnets while preventing inbound connections.
  • The first four IPs and last IP in each subnet CIDR are reserved by AWS (5 IPs total per subnet).
  • Route tables are evaluated when traffic leaves a subnet. Each subnet can be associated with only one route table at a time.

NAT Gateway vs NAT Instance

The exam may compare NAT Gateways (managed, highly available, scales automatically) with NAT Instances (self-managed EC2 instances). NAT Gateways are the recommended solution for production workloads. NAT Instances are legacy but still appear in exam questions as a cost-saving alternative.

Your VPC foundation is complete. You have a public subnet that can reach the internet directly and a private subnet that can only send outbound traffic through the NAT Gateway. Next up, you will add layered security to this architecture using security groups and NACLs.

Configure Security Groups and NACLs

Your VPC architecture is in place with public and private subnets, each with their own route tables. Now you need to control which traffic can actually reach the instances you'll launch inside those subnets.

AWS gives you two layers of network security to work with. Security groups act as virtual firewalls at the instance level, while Network ACLs (NACLs) operate at the subnet level. In this step, you'll configure both to create layered, defense-in-depth security for your private instance.

In this step, get ready to:
  • Create two security groups with specific access rules for the bastion host and the private instance.
  • Create a custom NACL with explicit allow and deny rules for the private subnet.
  • Understand the key exam differences between stateful security groups and stateless NACLs.
Create security groups for the bastion and private instance

Security groups control traffic at the instance level. You'll create one for the bastion host (allowing SSH from your IP) and one for the private instance (allowing SSH only from the bastion's security group).

  • In the VPC Console left navigation pane, choose Security groups.
  • Click Create security group.
  • In the Security group name field, enter Bastion-SG.
  • In the Description field, enter Security group for bastion host SSH access.
  • For VPC, select your exam-lab-vpc from the dropdown.
  • Under Inbound rules, click Add rule.
  • Set Type to SSH.
  • Set Source to My IP. This auto-fills your current public IP address.
  • Leave the outbound rules as the default (all traffic allowed).
  • Click Create security group.

Why use My IP instead of 0.0.0.0/0?

Selecting My IP restricts SSH access to only your current public IP address. Using 0.0.0.0/0 would allow SSH from any IP on the internet, which is a serious security risk in production.

Now create the second security group for the private instance.

  • Click Create security group again.
  • In the Security group name field, enter Private-SG.
  • In the Description field, enter Security group for private instance SSH from bastion.
  • For VPC, select your exam-lab-vpc.
  • Under Inbound rules, click Add rule.
  • Set Type to SSH.
  • For Source, start typing Bastion-SG in the search box and select the Bastion-SG security group from the dropdown.
  • Leave the outbound rules as the default (all traffic allowed).
  • Click Create security group.

Why reference a security group as the source?

When you specify a security group ID as the source, any instance associated with that security group can send traffic on that port. This is more flexible than hardcoding IP addresses because it works dynamically as instances are launched or terminated.

This is a key SAA-C03 exam concept for designing scalable security architectures.

Don't see your VPC in the dropdown?

Make sure you are in the same AWS Region where you created your VPC in Step 1. Check the Region selector in the top right corner of the console.

help me find my VPC when creating a security group

Create a custom NACL for the private subnet

NACLs add a second security layer at the subnet boundary. Unlike security groups, NACLs are stateless, which means you must explicitly allow return traffic.

  • In the VPC Console left navigation pane, choose Network ACLs.
  • Click Create network ACL.
  • In the Name field, enter Private-NACL.
  • For VPC, select your exam-lab-vpc.
  • Click Create network ACL.

Now add the inbound rules to your new NACL.

  • Select your new Private-NACL from the list.
  • Click the Inbound rules tab.
  • Click Edit inbound rules.
  • Click Add new rule.
  • Set Rule number to 100.
  • Set Type to SSH.
  • Set Source to 10.0.1.0/24 (your public subnet CIDR).
  • Set Allow/Deny to Allow.
  • Click Add new rule again for the second rule.
  • Set Rule number to 200.
  • Set Type to Custom TCP.
  • Set Port range to 1024-65535.
  • Set Source to 0.0.0.0/0.
  • Set Allow/Deny to Allow.
  • Click Save changes.

Why allow ephemeral ports 1024-65535?

NACLs are stateless. When the private instance sends a response back from an SSH session, the return traffic uses a random high-numbered port (an ephemeral port).

Without Rule 200, responses would be blocked even though the initial SSH connection was allowed by Rule 100. Security groups handle this automatically because they are stateful.

Now add the outbound rules.

  • Click the Outbound rules tab.
  • Click Edit outbound rules.
  • Click Add new rule.
  • Set Rule number to 100.
  • Set Type to All traffic.
  • Set Destination to 0.0.0.0/0.
  • Set Allow/Deny to Allow.
  • Click Save changes.

Finally, associate the NACL with your private subnet.

  • With Private-NACL still selected, click the Subnet associations tab.
  • Click Edit subnet associations.
  • Select the checkbox next to exam-lab-subnet-private1 (your private subnet with CIDR 10.0.2.0/24).
  • Click Save changes.

NACL not showing your subnet?

If your private subnet does not appear in the list, confirm you selected the correct VPC when creating the NACL. The NACL must belong to the same VPC as the subnet.

help me associate my NACL with my private subnet

Exam corner: security groups vs NACLs

Understanding the differences between security groups and NACLs is one of the most frequently tested topics on the SAA-C03 exam.

What is the difference between security groups and NACLs?

  • Security groups are stateful. Return traffic is automatically allowed regardless of outbound rules.
  • NACLs are stateless. Return traffic must be explicitly allowed with a separate rule.
  • Security groups operate at the instance level (attached to ENIs). NACLs operate at the subnet level.
  • Security groups support allow rules only. NACLs support both allow and deny rules.
  • Security groups evaluate all rules collectively and allow if any rule permits. NACLs evaluate rules in ascending numerical order and stop at the first match.

Exam scenario: accidental SSH exposure

Question: An administrator accidentally opened SSH to 0.0.0.0/0 in a security group. What additional layer could have prevented unauthorized access?

Answer: A NACL on the subnet that only allows SSH from specific trusted IP ranges acts as a backup defense layer. This is the defense-in-depth pattern you just built.

Exam scenario: NACL rule evaluation

Question: A NACL has Rule 100 that allows HTTP from 0.0.0.0/0 and Rule 50 that denies HTTP from 0.0.0.0/0. What happens to inbound HTTP traffic?

Answer: The traffic is denied. Rule 50 is evaluated first (lowest number wins) and denies the traffic before Rule 100 is ever reached.

Your layered security is now in place. Next up, you'll launch EC2 instances into these subnets and test the bastion host connection pattern.

Launch Instances with IAM Role and Bastion Pattern

Your network is secured with layered defenses. Now you need actual instances to live inside this architecture.

In this step, you will launch two EC2 instances that form the bastion host pattern. One instance sits in the public subnet as your gateway, and the other hides in the private subnet running your application workload. The private instance gets an IAM role to access S3 without storing any credentials on the machine.

In this step, get ready to:
  • Create an IAM role that grants S3 read access to EC2 instances.
  • Launch a bastion host in the public subnet with a public IP.
  • Launch a private application server with the IAM role attached.
Create an IAM role for the private EC2 instance

Your private instance needs to read from S3, but storing access keys on an instance is a security risk. IAM roles solve this by delivering temporary, auto-rotating credentials through the instance metadata service.

  • Open a new browser tab and navigate to the IAM Console by typing IAM in the AWS search bar at the top.
  • In the left navigation menu, click Roles.
  • Click Create role.
  • For Trusted entity type, select AWS service.
  • Under Use case, select EC2.
  • Click Next.
  • In the search box on the Add permissions page, type AmazonS3ReadOnlyAccess.
  • Check the box next to AmazonS3ReadOnlyAccess in the results.
  • Click Next.
  • For Role name, enter EC2-S3-ReadOnly-Role.
  • Click Create role.

You should see a green success banner confirming the role was created.

What is an instance profile?

An instance profile is a container for an IAM role that delivers credentials to an EC2 instance. When you create a role for EC2 in the console, AWS automatically creates an instance profile with the same name.

You can attach at most one IAM role to an EC2 instance at a time. The instance retrieves temporary credentials from the instance metadata service, so no long-term access keys are needed.

Don't see AmazonS3ReadOnlyAccess in the list?

Make sure you typed the full policy name AmazonS3ReadOnlyAccess without spaces. If the search returns no results, clear the filter and try typing just S3ReadOnly instead.

help me find the AmazonS3ReadOnlyAccess policy in the IAM console

Launch the bastion host in the public subnet

The bastion host is your only entry point into the private subnet. It sits in the public subnet with a public IP, acting as a gateway for SSH connections.

  • Navigate to the EC2 Console by typing EC2 in the AWS search bar.
  • Click Launch instance.
  • Under Name and tags, enter Bastion-Host.
  • Under Application and OS Images, confirm Amazon Linux 2023 AMI is selected (it should be the default).
  • Under Instance type, select t2.micro (Free tier eligible).
  • Under Key pair (login), click Create new key pair.
  • Enter exam-lab-key as the key pair name.
  • Keep the key pair type as RSA and format as .pem.
  • Click Create key pair. The exam-lab-key.pem file downloads automatically to your machine.

Save your key pair file

You will need this .pem file to SSH into your instances later. Remember where it downloaded (usually your Downloads folder). You cannot download it again after this point.

  • Under Network settings, click Edit.
  • For VPC, select your exam-lab-vpc.
  • For Subnet, select the public subnet (exam-lab-subnet-public1).
  • For Auto-assign public IP, select Enable.
  • For Firewall (security groups), select Select existing security group.
  • Choose Bastion-SG from the dropdown.
  • Click Launch instance.

You should see a success message confirming the instance is launching. Click View all instances to return to the instances list.

Don't see your VPC or subnet in the dropdown?

Check that you are in the same AWS Region where you created your VPC. The region selector is in the top-right corner of the console. Your VPC and subnets only appear in the region where they were created.

help me find my VPC when launching an EC2 instance

Launch the private instance with the IAM role

The private instance represents your application server. It has no public IP and can only be reached through the bastion. You will attach the IAM role so it can read from S3 using temporary credentials.

  • From the EC2 instances page, click Launch instance again.
  • Under Name and tags, enter Private-App-Server.
  • Under Application and OS Images, confirm Amazon Linux 2023 AMI is selected.
  • Under Instance type, select t2.micro.
  • Under Key pair (login), select the existing exam-lab-key key pair.
  • Under Network settings, click Edit.
  • For VPC, select exam-lab-vpc.
  • For Subnet, select the private subnet (exam-lab-subnet-private1).
  • For Auto-assign public IP, select Disable.
  • For Firewall (security groups), select Select existing security group.
  • Choose Private-SG from the dropdown.
  • Expand the Advanced details section at the bottom of the page.
  • For IAM instance profile, select EC2-S3-ReadOnly-Role from the dropdown.
  • Click Launch instance.

Click View all instances to return to your instances list. Wait for both instances to show Instance state as Running and Status check as 2/2 checks passed.

Why use an IAM role instead of access keys?

An IAM role provides temporary, automatically-rotated credentials to the instance through the instance metadata service. If a developer stored long-term access keys on the instance and the instance was compromised, those keys could be used from anywhere.

Roles eliminate that risk entirely. The temporary credentials expire and rotate without any intervention. This is a frequently tested concept on the SAA-C03 exam.

Exam scenario: EC2 needs access to DynamoDB

"An application on EC2 needs to access DynamoDB. The security team requires no long-term credentials stored on the instance. What should the architect recommend?"

Answer: Create an IAM role with DynamoDB permissions and attach it to the EC2 instance via an instance profile. The application uses the AWS SDK, which automatically retrieves temporary credentials from the instance metadata service.

Don't see EC2-S3-ReadOnly-Role in the IAM instance profile dropdown?

The instance profile can take a minute to propagate after role creation. Try refreshing the page or closing and reopening the Advanced details section. If it still doesn't appear, verify you created the role with EC2 as the trusted entity (not another service).

help me attach an IAM role to my EC2 instance at launch

Your bastion architecture is live. You have a public-facing gateway and a hidden private server with role-based S3 access. Next, you will connect through this architecture and prove the IAM role works by accessing S3 from the private instance.

Connect Through the Bastion and Verify IAM Role Access

Your instances are running, your security groups are locked down, and your IAM role is attached. Now it is time to prove the architecture works.

In this step, you will SSH into the bastion host from your Windows machine, then hop from the bastion into the private instance. Once inside, you will verify that the IAM role delivers temporary credentials without any stored access keys.

In this step, get ready to:
  • SSH from your Windows machine to the bastion host in the public subnet.
  • Jump from the bastion to the private instance using the bastion host pattern.
  • Verify that the private instance can access S3 using IAM role credentials.
SSH to the bastion host from your Windows machine

To reach the private instance, you first need to get onto the bastion host. The bastion sits in the public subnet and is the only entry point into your private network.

  • Return to the EC2 Console and click Instances in the left navigation pane.
  • Select the Bastion-Host instance.
  • Under the Details tab in the lower panel, find the Public IPv4 address field and copy the IP address.

Why do I need this IP address?

The bastion's public IPv4 address is what allows your local machine to reach it over the internet. The private instance has no public IP, so you cannot connect to it directly.

  • Press the Windows key to open your search bar.
  • Type PowerShell and press Enter to open it.
  • Connect to the bastion host by running this command:
ssh -i [[PEMPATH="path\\to\\exam-lab-key.pem"]] ec2-user@[[BASTION_PUBLIC_IP="your bastion public IPv4 address"]]

What does this command do?

The -i flag tells SSH which private key file to use for authentication. ec2-user is the default username for Amazon Linux instances.

Replace the path with the actual location of your exam-lab-key.pem file on your machine. For example, if you saved it to your Downloads folder, the path would be C:\Users\YourName\Downloads\exam-lab-key.pem.

Seeing a fingerprint prompt? Do not panic

The first time you connect, SSH displays a message asking if you want to continue connecting. This is normal for a first-time connection. Type yes and press Enter to accept the host key and proceed.

You should see your terminal prompt change to something like [ec2-user@ip-10-0-1-... ~]$. This confirms you are connected to the bastion host.

Connection timed out or refused?

  • Verify that your Bastion-SG security group allows SSH (TCP 22) from your current IP address. Your IP may have changed since you created the rule.
  • Confirm the Bastion-Host instance state is Running in the EC2 Console.
  • Make sure the path to your .pem file is correct and the file has not been moved or renamed.

Still stuck? Help me troubleshoot my SSH connection to the bastion host.

SSH from the bastion to the private instance

You are now on the bastion. The next hop takes you into the private subnet. This is the jump box pattern: you jump from a trusted, internet-facing host to an internal host that has no direct internet access.

To SSH from the bastion to the private instance, you need your key file available on the bastion. The simplest approach is to recreate the key file on the bastion by pasting its contents.

  • On your local Windows machine, open the exam-lab-key.pem file in Notepad or any text editor.
  • Select all the text (the entire file contents including the header and footer lines) and copy it.
  • Back in your bastion SSH session, create the key file by running:
nano exam-lab-key.pem
  • Right-click in the terminal window to paste the key content into nano.
  • Press Ctrl+X to exit nano.
  • Press Y to confirm saving.
  • Press Enter to accept the filename.
  • Set the correct permissions on the key file by running:
chmod 400 exam-lab-key.pem

Why chmod 400?

SSH requires that private key files are only readable by the owner. The chmod 400 command sets the file to read-only for you and no access for anyone else. Without this, SSH refuses to use the key.

  • Now you need the private IP of your Private-App-Server. Switch back to the EC2 Console in your browser.
  • Select the Private-App-Server instance.
  • Under the Details tab, find the Private IPv4 addresses field and copy the IP address.
  • Return to your bastion SSH session and connect to the private instance by running:
ssh -i exam-lab-key.pem ec2-user@[[PRIVATE_IP="your private instance private IPv4 address"]]

Seeing a fingerprint prompt again?

Just like before, type yes and press Enter to accept the host key for the private instance.

Your terminal prompt should now show a different IP address, confirming you are on the private instance. You have successfully jumped through the bastion.

  • Verify the IAM role credentials by running:
aws sts get-caller-identity

You should see JSON output containing the role ARN with EC2-S3-ReadOnly-Role in it. This proves the instance is using temporary credentials from the IAM role.

  • Test S3 access by running:
aws s3 ls

This command lists your S3 buckets. It works because the IAM role grants S3 read access without any stored credentials on the instance.

What just happened?

The private instance retrieved temporary security credentials from the instance metadata service. These credentials come from the EC2-S3-ReadOnly-Role that you attached via the instance profile.

The AWS CLI automatically uses these credentials without any configuration. This is the recommended approach on the SAA-C03 exam. Never store long-term access keys on an EC2 instance.

aws s3 ls not working?

  • If you see a timeout error, check that the private subnet route table has a route for 0.0.0.0/0 pointing to the NAT Gateway. Without outbound internet access, the instance cannot reach the S3 API endpoints.
  • If you see an access denied error, confirm the EC2-S3-ReadOnly-Role is attached to the instance. In the EC2 Console, select Private-App-Server and check the IAM Role field under Details.
  • If aws sts get-caller-identity shows no role ARN, the instance profile may not be attached. Go to Actions, then Security, then Modify IAM role to attach it.

Still stuck? Help me troubleshoot IAM role access on my private EC2 instance.

Exam Corner: bastion host and IAM roles

You have just implemented two patterns that appear frequently on the SAA-C03 exam. Let's connect what you did to the types of questions you will see.

Exam scenario: secure access to private instances

Question: How should a solutions architect provide secure administrative access to instances in private subnets?

Answer: Deploy a bastion host (jump box) in a public subnet with a security group allowing SSH only from trusted IPs. Private instances allow SSH only from the bastion's security group. This is exactly what you built.

Exam scenario: credentials on EC2 without stored keys

Question: An application on EC2 needs to access DynamoDB. The security team requires no long-term credentials stored on the instance. What should the architect recommend?

Answer: Create an IAM role with DynamoDB permissions and attach it to the EC2 instance via an instance profile. The application uses the AWS SDK, which automatically retrieves temporary credentials from the instance metadata service.

Key exam fact: instance profiles

An instance profile is a container for an IAM role. It is the mechanism that delivers IAM role credentials to EC2 instances. When you create a role for EC2 in the console, AWS automatically creates a matching instance profile with the same name as the role.

Your bastion host architecture is working. You can only reach the private instance by hopping through the bastion, and the private instance accesses AWS services using temporary IAM role credentials. Next up, you will explore Elastic Network Interfaces and see how a single instance can hold multiple IP addresses.

Explore Elastic Network Interfaces (ENIs)

In the previous step, you proved that the bastion host pattern works. You connected to the private instance through the bastion and confirmed that the IAM role provides S3 access without any stored credentials.

Now you will explore a powerful networking feature that unlocks advanced architectures. Elastic Network Interfaces (ENIs) are virtual network cards you can create, attach, and move between instances. They enable use cases like failover, traffic separation, and multi-homed configurations that are heavily tested on the SAA-C03 exam.

In this step, get ready to:
  • Create a secondary ENI in the private subnet and attach it to your instance.
  • Observe multiple IPs and MAC addresses on the private instance.
  • Understand ENI failover use cases for the SAA-C03 exam.
Create a secondary ENI in the private subnet

Every EC2 instance launches with a primary ENI attached. You cannot detach the primary ENI, but you can create additional ENIs and attach them to add more network interfaces to the same instance.

  • Switch to your browser and return to the AWS Management Console.
  • In the EC2 Console, click Network Interfaces in the left navigation menu under Network & Security.
  • Click Create network interface.
  • For Description, enter Secondary-ENI.
  • For Subnet, select your private subnet (exam-lab-subnet-private1).
  • For Security groups, select Private-SG.
  • Click Create network interface.

Note the private IP address assigned to this new ENI. It will be different from your Private-App-Server's primary IP.

Why the same subnet?

You must attach a network interface to an instance in the same Availability Zone. Since your Private-App-Server lives in exam-lab-subnet-private1, the secondary ENI must also be created in that subnet.

  • Select the newly created ENI (Secondary-ENI) by checking its checkbox.
  • Click Actions, then click Attach.
  • In the Instance dropdown, select Private-App-Server.
  • Click Attach.

The ENI status changes to in-use. Your private instance now has two network interfaces.

ENI won't attach?

Confirm that the ENI was created in the same subnet as your Private-App-Server. ENIs cannot be attached across Availability Zones.

Also check that the instance is in a running state. You cannot attach an ENI to a terminated instance.

Help me attach my secondary ENI to the private instance.

Observe the ENI behavior and multiple IPs

With the secondary ENI attached, the private instance now has two network interfaces. Each has its own private IP and MAC address. You can verify this from inside the instance.

  • Switch back to your SSH session on the Private-App-Server (through the bastion).
  • View both network interfaces by running this command:
ip addr show

What should I see?

You should see two interfaces listed: eth0 (the primary ENI) and eth1 (the secondary ENI you just attached). Each has its own inet address (private IP) and its own MAC address in the link/ether line.

  • Confirm both MAC addresses are visible in instance metadata by running these commands:
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/network/interfaces/macs/

What does this command do?

The first line requests a session token from the Instance Metadata Service (IMDSv2). The second line uses that token to query the network interfaces metadata endpoint.

You should see two MAC addresses listed, one for each ENI. Each ENI's metadata (subnet, security groups, IP addresses) is accessible under its MAC address path.

Why can each ENI have its own security group?

Security groups are associated with ENIs, not with individual IP addresses or instances directly. This means you can run management traffic (SSH) on one ENI with a restrictive security group, and application traffic on another ENI with different rules. This separation is a key architecture pattern for the exam.

Getting an empty response or error?

If you see a 401 error or empty output from the metadata command, make sure you ran both lines together. The TOKEN variable must be set in the same shell session before the second curl command.

If you only see one MAC address, wait 30 seconds and try again. The secondary ENI metadata can take a moment to appear after attachment.

Help me query instance metadata to see both ENI MAC addresses.

Exam Corner: ENI use cases

ENIs are a frequently tested topic on the SAA-C03 exam. Understanding when and why to use them separates basic EC2 knowledge from solutions architect thinking.

What are the key ENI use cases for the exam?

  • Management network separation: SSH on one ENI, application traffic on another, each with different security groups.
  • Low-budget high-availability failover: detach an ENI from a failed instance and attach it to a standby. The ENI keeps its private IP, Elastic IP, and MAC address.
  • Network appliances: load balancers and NAT instances use multiple interfaces to bridge traffic between subnets.
  • Dual-homed instances: an instance with ENIs in two different subnets can communicate on both networks.

Exam Scenario: database failover with ENIs

Question: A database instance fails. The replacement instance needs the same private IP to avoid updating all application connection strings. How can this be achieved?

Answer: Use a secondary ENI with the database IP. When the instance fails, detach the ENI and attach it to the replacement instance. The ENI retains its private IP, Elastic IP, and MAC address, so network traffic flows to the standby instance immediately.

Key ENI facts for the exam

  • The primary ENI cannot be detached from an instance.
  • Secondary ENIs can be detached and reattached to another instance in the same Availability Zone.
  • ENIs retain their private IPs, Elastic IPs, and MAC addresses when moved between instances.
  • Security groups are associated with ENIs, not individual IP addresses.

Your private instance now has two fully functional network interfaces. Next up in the Secret Mission, you will create all three placement group types, verify enhanced networking, and simulate an ENI failover scenario.

Secret mission

Placement Groups, Enhanced Networking, and ENI Failover

You've built a secure VPC architecture with layered security and bastion access. Now take it further by exploring three types of placement groups, verifying enhanced networking on your instances, and simulating an ENI failover scenario. These are frequently tested SAA-C03 concepts that appear across multiple exam domains.

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. The NAT Gateway is the primary cost driver in this project, charging $0.045 per hour plus $0.045 per GB of data processed.

Cost warning

Your NAT Gateway costs approximately $1.08 per day while running. If you are not actively using these resources, choose Pause or Delete below to stop charges from accumulating.

EC2 t2.micro instances are free-tier eligible for accounts less than 12 months old (750 hours/month). If your account is outside the free tier, each instance costs approximately $0.0116 per hour.

Resources you used:

  • NAT Gateway (exam-lab-nat-public1) with associated Elastic IP.
  • EC2 instances: Bastion-Host and Private-App-Server.
  • Secondary ENI (Secondary-ENI) attached to Private-App-Server.
  • Placement groups: hpc-cluster-pg, hadoop-partition-pg, critical-spread-pg.
  • VPC (exam-lab-vpc) including subnets, route tables, Internet Gateway, security groups, and NACLs.
  • IAM role: EC2-S3-ReadOnly-Role.
  • Local key pair file (exam-lab-key.pem) on your Windows machine.

Keep everything running

No action needed. Choose this if you are still actively building or plan to continue working with this VPC architecture today.

  • Your NAT Gateway will continue to incur charges at $0.045/hour (approximately $1.08/day).
  • EC2 t2.micro instances are free-tier eligible. If you have exceeded the free tier, they cost approximately $0.0116/hour each.
  • The Elastic IP associated with the running NAT Gateway does not incur additional charges.
  • The VPC, subnets, route tables, security groups, NACLs, and placement groups are all free to keep.

Pause - I'll come back to this later

Shut down the cost-generating resources while keeping your VPC architecture intact for reference. You can recreate the NAT Gateway and instances when you return.

  • In the EC2 Console, select Instances from the left menu.
  • Select both Bastion-Host and Private-App-Server, click Instance state, then Terminate instance.
  • Navigate to VPC Console, select NAT gateways from the left menu.
  • Select exam-lab-nat-public1, click Actions, then Delete NAT gateway. Type delete to confirm.
  • Wait for the NAT Gateway status to show Deleted. Then go to Elastic IPs in the VPC Console left menu.
  • Select the Elastic IP that was associated with the NAT Gateway, click Actions, then Release Elastic IP addresses. Confirm the release.
  • Your VPC, subnets, route tables, security groups, NACLs, and placement groups remain free and intact for when you return.

Delete - I don't want to use this again

Remove all project resources completely. This returns your AWS charges to $0.

Follow these steps in order:

  • In the EC2 Console, select Instances from the left menu. Select both Bastion-Host and Private-App-Server, click Instance state, then Terminate instance. Confirm the termination.
  • In the EC2 Console left menu, select Network Interfaces. Select Secondary-ENI, click Actions, then Delete. If it is still attached, detach it first by choosing Actions, Detach, then delete.
  • Navigate to the VPC Console. Select NAT gateways from the left menu. Select exam-lab-nat-public1, click Actions, then Delete NAT gateway. Type delete to confirm.
  • Wait for the NAT Gateway status to show Deleted. Then select Elastic IPs in the VPC Console left menu. Select the Elastic IP that was associated with the NAT Gateway, click Actions, then Release Elastic IP addresses. Confirm the release.
  • In the EC2 Console, select Placement Groups from the left menu. Select each placement group (hpc-cluster-pg, hadoop-partition-pg, critical-spread-pg), click Actions, then Delete placement group. Confirm each deletion.
  • In the VPC Console, select Your VPCs from the left menu. Select exam-lab-vpc, click Actions, then Delete VPC. This automatically deletes the associated subnets, route tables, Internet Gateway, security groups, and NACLs. Confirm the deletion.
  • Navigate to the IAM Console. Select Roles from the left menu. Search for and select EC2-S3-ReadOnly-Role. Click Delete at the top of the page. Enter the role name to confirm and click Delete. This also removes the associated instance profile.
  • Delete the exam-lab-key.pem file from your Windows machine. Navigate to the folder where you downloaded it and delete the file.

NAT Gateway deletion takes a moment

After you delete the NAT Gateway, its status may show as Deleting for a few minutes. Wait until it shows Deleted before you attempt to release the Elastic IP. If the Release option is greyed out, the NAT Gateway has not finished deleting yet.

Nice Work!

Nice Work!

You just built a production-style secure multi-tier VPC architecture from scratch. This is the exact pattern that real-world AWS environments use to protect sensitive workloads, and it maps directly to what the SAA-C03 exam tests.

Here's what you accomplished:

  • Built a custom VPC with public and private subnets, configured layered security using security groups (stateful, instance-level) and NACLs (stateless, subnet-level), and set up proper route tables with an Internet Gateway and NAT Gateway.
  • Implemented the bastion host pattern to securely access a private instance, and granted it S3 access through an IAM role with no hardcoded credentials.
  • Created and attached a secondary ENI, observed multiple private IPs on a single instance, and understood how ENI detach/reattach enables failover without changing connection strings.
  • Secret Mission: Created all three placement group types (cluster, partition, spread), verified enhanced networking (ENA) on your instances, and simulated an ENI failover scenario.

Ready to quiz yourself?