Table of contents
This is the most recommended approach by AWS for secure deployment of applications in EC2 instance
About the Project
This example demonstrates how to create a VPC that you can use for servers in a production environment. To improve resiliency, you deploy the servers in two Availability Zones, by using an Auto Scaling group and an Application Load Balancer. For additional security, you deploy the servers in private subnets. The servers receive requests through the load balancer. The servers can connect to the internet by using a NAT gateway. To improve resiliency, you deploy the NAT gateway in both Availability Zones.
Overview
The following diagram provides an overview of the resources included in this example.
The VPC has public subnets and private subnets in two Availability Zones.
Each public subnet contains a NAT gateway and a load balancer node.
The servers run in the private subnets, are launched and terminated by using an Auto Scaling group, and receive traffic from the load balancer.
The servers can connect to the internet by using the NAT gateway.
The servers can connect to Amazon S3 by using a gateway VPC endpoint.
While creating VPC, you see below options, you have to choose either of one
1) Option VPC (Basic VPC):
Creates a minimal VPC setup with only the essential components:
A single VPC with a default CIDR block.
No additional subnets, route tables, or gateways are created.
Suitable if you want a clean slate to configure all components manually.
2) Option VPC and More (With additional resources):
Creates a VPC with several predefined components:
Default subnets in multiple Availability Zones.
An internet gateway attached to the VPC.
Route tables with routes for internet access.
Network ACLs and security groups set up with default rules.
This option is ideal for users who want a VPC ready for typical workloads with minimal setup effort.
Create the VPC
Use the following procedure to create a VPC with a public subnet and a private subnet in two Availability Zones, and a NAT gateway in each Availability Zone.
To create the VPC
Open the Amazon VPC console at https://console.aws.amazon.com/vpc/.
On the dashboard, choose Create VPC.
For Resources to create, choose VPC and more.
Configure the VPC
For Name tag auto-generation, enter a name for the VPC.
For IPv4 CIDR block, you can keep the default suggestion, or alternatively you can enter the CIDR block required by your application or network. (*use IPV4 for this project)
If your application communicates by using IPv6 addresses, choose IPv6 CIDR block, Amazon-provided IPv6 CIDR block.(* not needed for this project)
Configure the subnets
For Number of Availability Zones, choose 2, so that you can launch instances in multiple Availability Zones to improve resiliency.
For Number of public subnets, choose 2.
For Number of private subnets, choose 2.
You can keep the default CIDR block for the public subnet, or alternatively you can expand Customize subnet CIDR blocks and enter a CIDR block. For more information, see Subnet CIDR blocks.( *keep as default)
For NAT gateways, choose 1 per AZ to improve resiliency.
For VPC endpoints, if your instances must access an S3 bucket, keep the S3 Gateway default. Otherwise, instances in your private subnet can't access Amazon S3. There is no cost for this option, so you can keep the default if you might use an S3 bucket in the future. If you choose None, you can always add a gateway VPC endpoint later on.( *for this project we are not using S3 bucket as we are focusing on creating VPC, so select none)
For DNS options, keep DNS options as default
Choose Create VPC.
PREVIEW of VPC
Deploy your application
Ideally, you've finished testing your servers in a development or test environment, and created the scripts or images that you'll use to deploy your application in production.
You can use Amazon EC2 Auto Scaling to deploy servers in multiple Availability Zones and maintain the minimum server capacity required by your application.
To launch instances by using an Auto Scaling group
Create a launch template to specify the configuration information needed to launch your EC2 instances by using Amazon EC2 Auto Scaling. For step-by-step directions, see Create a launch template for your Auto Scaling group in the Amazon EC2 Auto Scaling User Guide.
Create an Auto Scaling group, which is a collection of EC2 instances with a minimum, maximum, and desired size. For step-by-step directions, see Create an Auto Scaling group using a launch template in the Amazon EC2 Auto Scaling User Guide.
Create a load balancer, which distributes traffic evenly across the instances in your Auto Scaling group, and attach the load balancer to your Auto Scaling group. For more information, see the Elastic Load Balancing User Guide and Use Elastic Load Balancing in the Amazon EC2 Auto Scaling User Guide.
Detailed Explanation :
1) Create launch template:
Open the Amazon EC2 console at https://console.aws.amazon.com/ec2/.
On the navigation pane, under Instances, choose Launch Templates.
Enter a Name(eg:prod-example) and provide a description(eg: proof of concept for app deploying in AWS private subnet) for the initial version of the launch template.
(Optional) Under Auto Scaling guidance, select the check box to have Amazon EC2 provide guidance to help create a template to use with Amazon EC2 Auto Scaling. (*enable)
Under Launch template contents, fill out each required field and any optional fields as needed.
a) Application and OS Images (Amazon Machine Image): (Required) Choose the ID of the AMI for your instances. You can search through all available AMIs, or select an AMI from the Recents or Quick Start list.
b) For Instance type, choose a single instance type that's compatible with the AMI that you specified.(*select t2.micro which is a free tier)
c) Key pair (login): For Key pair name, choose an existing key pair, or choose Create new key pair to create a new one.
d) Network settings:
→ Subnet :Don’t change anything, keep default(* Don’t include in launch template)
→ For Firewall (security groups), use one or more security groups if exists, else create Security groupCreate Security group:
1. Enter Name and Description of security group.
2. VPC : Select VPC in which you want to launch ASG instances( the one which you created above)
3.Inbound Security Group Rules: you can allow “All Traffic”, but its not best practice. you should open ports that are required.
a) SSH port(22) : open this port for SSH access and select “Source type” : Anywhere
b) Custom TCP : this port is for the application that are deployed in your instances. select “Port r range” according to your needs (eg : 8000)and select “Source type” : Anywhere
Note: If you don't specify any security groups in your launch template, Amazon EC2 uses the default security group for the VPC that your Auto Scaling group will launch instances into. By default, this security group doesn't allow inbound traffic from external networksEBS Volumes: if you don’t want to add (*keep the settings as default)
Resource Tags and Advanced details: (*keep the settings as default)
Click on Create launch template
2) Create ASG using launch Template
When you create an Auto Scaling group, you must specify the necessary information to configure the Amazon EC2 instances, the Availability Zones and VPC subnets for the instances, the desired capacity, and the minimum and maximum capacity limits.
Prerequisites
- You must have created a launch template.
Steps to create ASG using launch template:
Open the Amazon EC2 console at https://console.aws.amazon.com/ec2/, and choose Auto Scaling Groups from the navigation pane.
On the navigation bar at the top of the screen, choose the same AWS Region that you used when you created the launch template.
Choose Create an Auto Scaling group.
On the Choose launch template or configuration page, do the following:
For Auto Scaling group name, enter a name for your Auto Scaling group.
For Launch template, choose an existing launch template.(*select the one which you have created above)
For Launch template version, choose whether the Auto Scaling group uses the default, the latest, or a specific version of the launch template when scaling out.(*select default)
Verify that your launch template supports all of the options that you are planning to use, and then choose Next.
On the Choose instance launch options page,
Don’t override instance type requirements, keep the same instance attributes or instance type from your launch template
Under Network, for VPC, choose a VPC. The Auto Scaling group must be created in the same VPC as the security group you specified in your launch template.
For Availability Zones and subnets, choose one or more subnets( *select private subnets in two availability zone because we launch our application in private subnet) in the specified VPC. Use subnets in multiple Availability Zones for high availability .
Choose Next to continue to the next step.
On the Configure advanced options page,
for now select No load balancer , for this project we are not attaching load balancer in ASG. we will create Application Load Balancer (ALB) in public subnet.
VPC Lattice integration options ; Health checks ; Additional settings (* Dont configure anything ,keep as default) and click on Next
(Optional) On the Configure group size and scaling policies page, configure the following options, and then choose Next:
Under Group size, for Desired capacity, enter the initial number of instances to launch.(*select 2)
In the Scaling section, under Scaling limits, if your new value for Desired capacity is greater than Min desired capacity and Max desired capacity, the Max desired capacity is automatically increased to the new desired capacity value. You can change these limits as needed
Scaling policies ; Instance scale-in protection (* Dont configure anything ,keep as default ) and
Notification and Tags (optional )
click on Next and Launch your ASG
After launching ASG, verify whether ASG has created instances in private subnets of each availablity zone
-> Note : Before creating ALB, you have to install the application inside the server (instances)
Install Application in EC2 instances:
EC2 instances in a private subnet do not have public IPs, so they cannot be accessed directly. To access these instances, a Bastion host is used. The Bastion host is placed in a public subnet, allowing secure access to the private subnet.
First we will SSH into bastion host, from bastion host we will SSH into private subnet
1) Launch the Bastion Host
Go to the EC2 Dashboard:
- Click on Launch Instance.
Choose AMI:
- Select an Amazon Linux or any other Linux/Windows AMI.
Choose Instance Type:
- Select an appropriate instance type (e.g.,
t2.micro
for testing).
- Select an appropriate instance type (e.g.,
Add Key Pair:
- Use an existing key pair or create a new one for SSH/RDP access.
Configure Network Settings:
Ensure Bastion Host is created in same VPC (*select required VPC in which you need to launch this instance otherwise you cannot access private subnets, if you launch in different VPC)
Assign the instance to the public subnet.
Enable Auto-assign Public IP.
Add Security Group:
- Create a security group to allow SSH (port 22) or RDP (if Windows) from your trusted IP range (e.g., your office/home IP).
Configure Storage and Additional settings (* keep as default)
Launch the instance
Why Copy a .pem
File to a Bastion Host?
No Direct Access to Private Instances:
- If private instances are in a VPC and do not have public IPs, they can only be accessed through the Bastion host, which serves as a gateway to the private network.
SSH Key Required for Access:
- Private instances may require the
.pem
key for SSH authentication. Without the.pem
file on the Bastion host, it can be difficult to connect to the private instances from there.
- Private instances may require the
2)To copy a .pem
file from your personal laptop to an AWS Bastion host, follow these detailed steps:
Step 1: Prerequisites
SSH Access to the Bastion Host:
- Ensure you have the
.pem
file (key pair) for the Bastion host. This is required to SSH into it. If you don't have the private key for the Bastion host, refer to key recovery or access alternatives (e.g., AWS Systems Manager).
- Ensure you have the
Public IP of the Bastion Host:
- Obtain the public IP address of your Bastion host from the AWS EC2 console.
Trusted IP Configuration:
- Ensure the security group for the Bastion host allows SSH (port 22) from your personal laptop's public IP. Use services like
https://whatismyipaddress.com
to find your IP.
- Ensure the security group for the Bastion host allows SSH (port 22) from your personal laptop's public IP. Use services like
.pem
File to Transfer:Identify the
.pem
file on your laptop that you need to transfer to the Bastion host. Ensure this file has proper permissions:chmod 400 /path/to/private-key.pem
Step 2:Use scp
to Transfer the .pem
File
The scp
(Secure Copy Protocol) command allows you to transfer files securely to the Bastion host over SSH.
Run the
scp
Command:scp -i /path/to/bastion-key.pem /path/to/private-key.pem ec2-user@<bastion-public-ip>:/home/ec2-user/
Replace:
/path/to/bastion-key.pem
: Path to the Bastion host's key pair./path/to/private-key.pem
: Path to the.pem
file on your laptop that you need to copy.<bastion-public-ip>
: Public IP of the Bastion host./home/ec2-user/
: Destination directory on the Bastion host.
Step 3: Test SSH Connectivity to the Bastion Host
Use the
ssh
command to connect to the Bastion host:ssh -i /path/to/bastion-key.pem ec2-user@<bastion-public-ip>
Replace:
/path/to/bastion-key.pem
: Path to the key pair for the Bastion host.<bastion-public-ip>
: Public IP of the Bastion host.After running the command, verify the file is copied:
ls
Step 4: Set Correct Permissions on the .pem
File
Set Permissions for the Copied File:
Make the
.pem
file readable only by the owner:chmod 400 /home/ec2-user/private-key.pem
Step 5: Use the .pem
File to Access Private Instances
SSH from the Bastion Host to the Private Instance:
Use the
.pem
file to SSH into a private EC2 instance:ssh -i /home/ec2-user/private-key.pem ec2-user@<private-instance-private-ip>
Replace
<private-instance-private-ip>
with the private IP address of the EC2 instance in the private subnet.
Confirm Access:
- Verify that you are now connected to the private instance.
Step 6: Clean Up
Delete the
.pem
File from the Bastion Host:Once you're done, delete the
.pem
file from the Bastion host to minimize security risks:rm /home/ec2-user/private-key.pem
Restrict Access:
- Update the security group for the Bastion host to allow SSH access only from trusted IP addresses.
Troubleshooting
Permission Denied:
If you see a "Permission denied" error while SSHing, check the permissions of your
.pem
files on your laptop and Bastion host:chmod 400 /path/to/private-key.pem chmod 400 /home/ec2-user/private-key.pem
Incorrect IP:
- Ensure your laptop’s public IP matches the allowed IP in the Bastion host's security group.
Firewall/Route Issues:
- Confirm that the private instance’s security group allows SSH from the Bastion host's private IP.
Security Best Practices
Avoid Persistent Keys:
- Use AWS Systems Manager Session Manager to manage instances without needing to copy
.pem
files.
- Use AWS Systems Manager Session Manager to manage instances without needing to copy
Logging:
- Enable logging on the Bastion host to monitor SSH activities.
Secure
.pem
Files:- Never share
.pem
files publicly and always store them securely.
- Never share
3)Install the python application in private EC2 instances
Install Python3 (Amazon Linux 2):
sudo yum update -y sudo yum install python3 -y
Start the HTTP server:
cd /var/www/html python3 -m http.server 8000
Test the server from a browser or
curl
:curl http://<private-ip>:8000
Note: if you want detailed explanation of above steps, go here
https://sruthipalle.hashnode.dev/step-by-step-guide-to-installing-apps-on-aws-private-ec2-instances
3) Create ALB and attach to ASG
→ ALB should be in Public Subnet and should be internet facing
Steps to Create an Application Load Balancer
1. Prerequisites
AWS Account: Ensure you have an active AWS account.
VPC Configuration: Identify the VPC and subnets where you want to deploy the ALB. The subnets should be in different availability zones for high availability.
Security Groups: Prepare security groups for the ALB and the backend target instances. The ALB security group should allow incoming HTTP/HTTPS traffic.
Target Instances: Ensure you have EC2 instances or a service running to register as targets in the load balancer.
2. Create the Application Load Balancer
Log in to AWS Management Console
- Navigate to the EC2 Dashboard.
Go to Load Balancers
In the left-hand menu, select Load Balancers under the "Load Balancing" section.
Click Create Load Balancer.
Choose Load Balancer Type
- Select Application Load Balancer.
Configure Basic Settings
Name: Enter a name for your ALB (e.g.,
my-app-alb
).Scheme: Choose Internet-facing: To route requests from the internet.
IP Address Type: Choose IPv4
Select Network Mappings
Select the VPC where the ALB will be deployed (*select VPC that you have created)
Choose at least two subnets in different availability zones for high availability (*choose public subnet in two availability zones)
Security Groups
- Assign a security group to the ALB. Ensure the group allows incoming traffic on the listener ports (e.g., 80 or 443).
Listeners and routing
Add one or more listeners (e.g., HTTP (port 80) or HTTPS (port 443)).
select HTTP→port 80
Create Target Group( where you will define which instances should be accessible)
Define a target group that the ALB will forward traffic to:
Target Type: Select Instances
Target group Name
Protocol: HTTP
VPC: (select VPC that you have created)
Protocol version: (*keep default)
Health Check Settings: Configure health checks to monitor target health (e.g.,
/health
endpoint). (*keep default)Set the thresholds for health checks (e.g., response timeout, success codes).
Register Targets
Add the targets (e.g., EC2 instances) to the target group.( Do not choose bastion Host)
click Include as pending below
Click on Create Target group
Select target group for port 80
Add on services and Load balancer tags (*keep as default)
Click on Create Load balancer
Confirm that the instances are marked as healthy in the target group.
Get ALB DNS Name
In the AWS Management Console, navigate to EC2 > Load Balancers.
Select your ALB and copy the DNS name (e.g.,
my-alb-1234567890.us-west-2.elb.amazonaws.com
).
Access the Website
Open a browser and visit the ALB DNS name:
http://my-alb-1234567890.us-west-2.elb.amazonaws.com
If you're using HTTPS, ensure the ALB has a valid SSL certificate configured in a listener.
Debugging Tips
ALB Health Check Failure: Review the health check path and port in the target group settings.
Error in Browser: Check logs on the Python webserver for errors and confirm the ALB is forwarding traffic correctly.
Test your configuration
After you've finished deploying your application, you can test it. If your application can't send or receive the traffic that you expect, you can use Reachability Analyzer to help you troubleshoot. For example, Reachability Analyzer can identify configuration issues with your route tables or security groups.
Clean up
When you are finished with this configuration, you can delete it. Before you can delete the VPC, you must delete the Auto Scaling group, terminate your instances, delete the NAT gateways, and delete the load balancer.
"Thank you for reading! I hope this blog sparked new ideas and insights. If you have questions or thoughts, drop a comment below. Until next time, keep learning and growing!"
Reach out to me at linkedin.com/in/sruthipalle
Happy Coding😊