This content originally appeared on DEV Community and was authored by bikash119
AWS VPC Infrastructure Setup Guide
Overview
This guide provides step-by-step instructions for creating a secure Virtual Private Cloud (VPC) infrastructure on AWS using the AWS CLI. A VPC enables you to provision a logically isolated section of the AWS Cloud where you can launch AWS resources in a virtual network that you define.
Prerequisites
- AWS CLI installed and configured with appropriate credentials
- Basic understanding of networking concepts (CIDR blocks, subnets)
- Appropriate IAM permissions for VPC and EC2 operations
Resource Tagging Strategy
Throughout this tutorial, we implement a consistent tagging strategy for all resources. This approach ensures:
- Easy resource identification and tracking
- Simplified cost allocation
- Better resource organization and management
Standard tags used:
-
Name
: Descriptive identifier for the resource -
Environment
: Development stage (e.g., Dev, Staging, Production) -
Purpose
: Project or application identifier
1. Virtual Private Cloud (VPC) Setup
Create the VPC
Create a VPC with a /16 CIDR block, providing approximately 65,536 IP addresses:
aws ec2 create-vpc \
--cidr-block 10.0.0.0/16 \
--tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=DoclingVPC},{Key=Environment,Value=Dev}]'
Note: The CIDR block 10.0.0.0/16
means all resources within this VPC will have IP addresses starting with 10.0.x.x
, where the first two octets remain constant.
Store VPC ID for Future Reference
VPC_ID=$(aws ec2 describe-vpcs \
--filters Name=tag-key,Values=Name \
--query "Vpcs[?Tags[?Key=='Name' && Value=='DoclingVPC']].VpcId" \
--output text)
Apply Additional Tags
Create a tags.json
file:
{
"Tags": [
{
"Key": "Purpose",
"Value": "DoclingSetup"
}
]
}
Apply the tags to the VPC:
aws ec2 create-tags --resources $VPC_ID --cli-input-json file://tags.json
Verify VPC Creation
aws ec2 describe-vpcs \
--filters Name=tag-key,Values=Purpose \
--query "Vpcs[].[VpcId,CidrBlock,State]" \
--output table
2. Subnet Configuration
Subnets segment your VPC into smaller networks. We’ll create both public and private subnets following AWS best practices.
Create Private Subnet
Deploy a private subnet in availability zone us-east-1b
:
aws ec2 create-subnet \
--vpc-id $VPC_ID \
--cidr-block 10.0.4.0/24 \
--availability-zone us-east-1b \
--tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=PrivateSubnet-1b}]'
Create Public Subnet
Deploy a public subnet in availability zone us-east-1a
:
aws ec2 create-subnet \
--vpc-id $VPC_ID \
--cidr-block 10.0.3.0/24 \
--availability-zone us-east-1a \
--tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=PublicSubnet-1a}]'
Note: Each /24 subnet provides 256 IP addresses (though AWS reserves 5 IPs per subnet for internal use).
Retrieve Subnet IDs
# Get Private Subnet ID
PRIVATE_SUBNET=$(aws ec2 describe-subnets \
--filters Name=tag-value,Values=PrivateSubnet-1b \
--query "Subnets[0].SubnetId" \
--output text)
# Get Public Subnet ID
PUBLIC_SUBNET=$(aws ec2 describe-subnets \
--filters Name=tag-value,Values=PublicSubnet-1a \
--query "Subnets[0].SubnetId" \
--output text)
Apply Tags to Subnets
aws ec2 create-tags --resources $PUBLIC_SUBNET --cli-input-json file://tags.json
aws ec2 create-tags --resources $PRIVATE_SUBNET --cli-input-json file://tags.json
Verify Subnet Configuration
aws ec2 describe-subnets \
--filters Name=tag-key,Values=Purpose \
--query "Subnets[].[SubnetId,CidrBlock,AvailabilityZone]" \
--output table
3. Internet Gateway Configuration
An Internet Gateway (IGW) enables communication between your VPC and the internet.
Create Internet Gateway
IGW_ID=$(aws ec2 create-internet-gateway \
--tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=DoclingIGW}]' \
--query "InternetGateway.InternetGatewayId" \
--output text)
Apply Tags
aws ec2 create-tags --resources $IGW_ID --cli-input-json file://tags.json
Attach Internet Gateway to VPC
aws ec2 attach-internet-gateway \
--internet-gateway-id $IGW_ID \
--vpc-id $VPC_ID
Verify Internet Gateway
aws ec2 describe-internet-gateways \
--filters Name=tag-key,Values=Purpose \
--query "InternetGateways[].[InternetGatewayId,Attachments[0].VpcId,Attachments[0].State]" \
--output table
4. Route Table Configuration
Route tables determine where network traffic is directed. AWS creates a main route table automatically with each VPC, but best practice dictates creating custom route tables for better security control.
Create Public Route Table
PUBLIC_ROUTE_TABLE_ID=$(aws ec2 create-route-table \
--vpc-id $VPC_ID \
--query "RouteTable.RouteTableId" \
--output text)
Add Internet Route
Configure the route table to direct internet-bound traffic through the Internet Gateway:
aws ec2 create-route \
--route-table-id $PUBLIC_ROUTE_TABLE_ID \
--destination-cidr-block 0.0.0.0/0 \
--gateway-id $IGW_ID
Associate Public Subnet with Route Table
PUBLIC_SUBNET_ASSOCIATION_ID=$(aws ec2 associate-route-table \
--route-table-id $PUBLIC_ROUTE_TABLE_ID \
--subnet-id $PUBLIC_SUBNET \
--query "AssociationId" \
--output text)
Verify Route Table Configuration
aws ec2 describe-route-tables \
--route-table-ids $PUBLIC_ROUTE_TABLE_ID \
--query "RouteTables[].[RouteTableId,Routes[].DestinationCidrBlock,Associations[].SubnetId]" \
--output table
Security Best Practices
Main Route Table: Never add internet routes to the main route table. This ensures new subnets don’t accidentally become public.
Subnet Isolation: Keep private subnets truly private by not associating them with route tables containing internet gateway routes.
Multi-AZ Deployment: For production environments, deploy subnets across multiple availability zones for high availability.
Network ACLs: Consider implementing Network Access Control Lists for additional subnet-level security.
AWS IAM Roles and Instance Profiles
AWS IAM operates on three fundamental components:
- Trust Relationship – Defines which entities can assume a role
- Permissions – Specifies what actions the role can perform
- Temporary Credentials – Provides time-limited access tokens for role assumption
IAM Implementation Process
- Create Trust Policy – Define which AWS services can assume the role
- Create Role – Establish the role with the trust policy
- Attach Permissions – Grant specific permissions to the role
- Create Instance Profile – Package the role for EC2 instances (when needed)
- Associate Resources – Link the role or instance profile to AWS resources
You may want to dive into the concepts here.
EC2 IAM Configuration
Trust Policy for EC2
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "ec2.amazonaws.com" },
"Action": "sts:AssumeRole"
}
]
}
Create EC2 Instance Role
# Create the role
aws iam create-role \
--role-name ec2_instance_role \
--assume-role-policy-document file://ec2-trust-policy.json \
--tags Key=Name,Value=EC2InstanceRole
# Add additional tags
aws iam tag-role \
--role-name ec2_instance_role \
--cli-input-json file://tags.json
# Attach managed policy
aws iam attach-role-policy \
--role-name ec2_instance_role \
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role
Create Instance Profile
# Create instance profile
aws iam create-instance-profile \
--instance-profile-name ec2_instance_role-profile \
--tags Key=Name,Value=EC2InstanceRoleProfile
# Add tags to instance profile
aws iam tag-instance-profile \
--instance-profile-name ec2_instance_role-profile \
--cli-input-json file://tags.json
# Associate role with instance profile
aws iam add-role-to-instance-profile \
--role-name ec2_instance_role \
--instance-profile-name ec2_instance_role-profile
ECS IAM Configuration
Trust Policy for ECS Tasks
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "ecs-tasks.amazonaws.com" },
"Action": "sts:AssumeRole"
}
]
}
ECS Task Execution Role
The task execution role is required for ECS to pull container images and write logs on your behalf.
# Create ECS task execution role
ECS_TASK_EXEC_ROLE=$(aws iam create-role \
--role-name ecs_task_exec_role \
--assume-role-policy-document file://ecs-trust-policy.json \
--tags Key=Name,Value=ECSTaskExecutionRole \
--query "Role.RoleName" \
--output text)
# Add tags
aws iam tag-role \
--role-name $ECS_TASK_EXEC_ROLE \
--cli-input-json file://tags.json
# Attach AWS managed policy for task execution
aws iam attach-role-policy \
--role-name $ECS_TASK_EXEC_ROLE \
--policy-arn arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
ECS Task Role (Optional)
The task role provides permissions for your application code running inside the container.
Note: This role can be omitted if your application doesn’t need AWS API access. Test without it first to determine if it’s necessary.
# Create ECS task role (if needed)
ECS_ROLE=$(aws iam create-role \
--role-name ecs_task_role \
--assume-role-policy-document file://ecs-trust-policy.json \
--tags Key=Name,Value=ECSTaskRole \
--query "Role.RoleName" \
--output text)
# Add tags
aws iam tag-role \
--role-name $ECS_ROLE \
--cli-input-json file://tags.json
# Attach application-specific policies as needed
# aws iam attach-role-policy --role-name $ECS_ROLE --policy-arn <policy-arn>
Summary
You now have the essential IAM & networking foundation for our AWS ECS deployment.
Next Steps: In the next part of this series, we’ll configure the EC2 infrastructure that will use these roles to run your containerized applications.
This content originally appeared on DEV Community and was authored by bikash119