Learn how to build a production-grade target cloud environment in AWS for migration. This comprehensive guide covers Multi-AZ VPC setup, RDS PostgreSQL databases, VPC peering, and networking configuration for cloud migration.
In this comprehensive guide, we’ll build the modern, production-grade AWS infrastructure that will serve as our migration destination. This includes a highly-available Multi-AZ architecture, VPC peering to connect the source and target environments, and all necessary networking configuration for seamless cloud migration.
Estimated Time: 50-70 minutes
Region: ap-south-1 (Mumbai)
Difficulty: Intermediate
Building the target environment is crucial for successful cloud migration. This hands-on approach gives you:
⚠️ Resources that will incur charges:
Estimated Phase 2 Cost: ~$1.00-1.50 per day while running
┌──────────────────────────────────────────────────────────────────┐
│ aws-target-vpc (10.1.0.0/16) │
│ │
│ ┌───────────────────────────────┬───────────────────────────┐ │
│ │ ap-south-1a │ ap-south-1b │ │
│ │ │ │ │
│ │ ┌──────────────────────────┐ │ ┌──────────────────────┐ │ │
│ │ │ Public Subnet │ │ │ Public Subnet │ │ │
│ │ │ 10.1.1.0/24 │ │ │ 10.1.2.0/24 │ │ │
│ │ │ │ │ │ │ │ │
│ │ │ (Reserved for ALB/NAT) │ │ │ (Reserved for ALB) │ │ │
│ │ └──────────────────────────┘ │ └──────────────────────┘ │ │
│ │ │ │ │
│ │ ┌──────────────────────────┐ │ ┌──────────────────────┐ │ │
│ │ │ Private Subnet │ │ │ Private Subnet │ │ │
│ │ │ 10.1.11.0/24 │ │ │ 10.1.12.0/24 │ │ │
│ │ │ │ │ │ │ │ │
│ │ │ (Migrated EC2 servers) │ │ │ (Future servers) │ │ │
│ │ └──────────────────────────┘ │ └──────────────────────┘ │ │
│ │ │ │ │
│ │ ┌──────────────────────────┐ │ ┌──────────────────────┐ │ │
│ │ │ Database Subnet │ │ │ Database Subnet │ │ │
│ │ │ 10.1.21.0/24 │ │ │ 10.1.22.0/24 │ │ │
│ │ │ │ │ │ │ │ │
│ │ │ ┌──────────────────┐ │ │ │ ┌──────────────────┐│ │ │
│ │ │ │ RDS Primary │◄───┼──┼─┼─►│ RDS Standby ││ │ │
│ │ │ │ production-db │ │ │ │ │ (Multi-AZ sync) ││ │ │
│ │ │ └──────────────────┘ │ │ │ └──────────────────┘│ │ │
│ │ └──────────────────────────┘ │ └──────────────────────┘ │ │
│ └───────────────────────────────┴───────────────────────────┘ │
│ │
│ Internet Gateway ←→ Public Subnets │
└──────────────────────────────────────────────────────────────────┘
▲
│ VPC Peering
▼
┌──────────────────────────────────────────────────────────────────┐
│ on-prem-vpc (10.0.0.0/16) │
│ (Source Environment from Phase 1) │
└──────────────────────────────────────────────────────────────────┘
This wizard will automatically create the VPC, subnets, route tables, and Internet Gateway.
VPC settings:
aws-target (it will auto-prefix resources)10.1.0.0/16Availability Zones:
Number of public subnets: 2
10.1.1.0/2410.1.2.0/24Number of private subnets: 4
10.1.11.0/2410.1.12.0/2410.1.21.0/2410.1.22.0/24NAT gateways: None (to save costs - ~$32/month)
VPC endpoints: None
DNS options:
On the right side, you’ll see a preview of all resources that will be created. Verify:
📝 Note down these IDs:
| Resource | Name | ID | CIDR |
|---|---|---|---|
| VPC | aws-target-vpc | vpc-xxxxxxxx | 10.1.0.0/16 |
| Public Subnet 1a | aws-target-subnet-public1-ap-south-1a | subnet-xxxxxxxx | 10.1.1.0/24 |
| Public Subnet 1b | aws-target-subnet-public2-ap-south-1b | subnet-xxxxxxxx | 10.1.2.0/24 |
| Private Subnet 1a | aws-target-subnet-private1-ap-south-1a | subnet-xxxxxxxx | 10.1.11.0/24 |
| Private Subnet 1b | aws-target-subnet-private2-ap-south-1b | subnet-xxxxxxxx | 10.1.12.0/24 |
| Private Subnet 1a (DB) | aws-target-subnet-private3-ap-south-1a | subnet-xxxxxxxx | 10.1.21.0/24 |
| Private Subnet 1b (DB) | aws-target-subnet-private4-ap-south-1b | subnet-xxxxxxxx | 10.1.22.0/24 |
| Internet Gateway | aws-target-igw | igw-xxxxxxxx | N/A |
The wizard creates generic names. Let’s rename them for clarity.
| Current Name | New Name | Purpose |
|---|---|---|
| aws-target-subnet-private1-ap-south-1a | target-app-subnet-1a | Migrated EC2 instances |
| aws-target-subnet-private2-ap-south-1b | target-app-subnet-1b | Migrated EC2 instances |
| aws-target-subnet-private3-ap-south-1a | target-db-subnet-1a | RDS databases |
| aws-target-subnet-private4-ap-south-1b | target-db-subnet-1b | RDS databases |
For the two database subnets, add an additional tag:
target-db-subnet-1aTypeDatabasetarget-db-subnet-1bGo to Security Groups in the left sidebar
Click Create security group
Configure:
target-web-sgSecurity group for migrated web servers in target VPCaws-target-vpcInbound rules - Add these rules:
Rule 1 (HTTP from anywhere):
Rule 2 (HTTPS from anywhere):
Rule 3 (SSH for management):
Rule 4 (Allow from source VPC for testing):
10.0.0.0/16Outbound rules: Leave default (All traffic)
Click Create security group
📝 Note the Security Group ID: sg-xxxxxxxxweb
Click Create security group again
Configure:
target-db-sgSecurity group for production Multi-AZ databaseaws-target-vpcInbound rules - Add these rules:
Rule 1 (PostgreSQL from web servers):
target-web-sg (the security group we just created)Rule 2 (PostgreSQL from source VPC for DMS):
10.0.0.0/16Outbound rules: Leave default
Click Create security group
📝 Note the Security Group ID: sg-xxxxxxxxdb
Click Create security group again
Configure:
target-dms-sgSecurity group for DMS replication instanceaws-target-vpcInbound rules: None needed
Outbound rules: Leave default (All traffic)
Click Create security group
📝 Note the Security Group ID: sg-xxxxxxxxdms
Before setting up peering, we need to prepare the source security groups.
on-prem-db-sg (from Phase 1)10.1.0.0/16 (target VPC CIDR)VPC Peering allows private communication between the two VPCs without going through the internet.
on-prem-to-target-peeringon-prem-vpc (10.0.0.0/16)aws-target-vpc (10.1.0.0/16)Now we need to add routes in both VPCs to route traffic through the peering connection.
Update Private Route Table (for database connectivity):
on-prem-private-rt (from Phase 1)10.1.0.0/16 (target VPC CIDR)on-prem-to-target-peeringUpdate Public Route Table (for web server connectivity):
on-prem-public-rt10.1.0.0/16on-prem-to-target-peeringUpdate Public Route Table:
aws-target-vpc that’s associated with public subnetsaws-target-rtb-public10.0.0.0/16 (source VPC CIDR)on-prem-to-target-peeringUpdate Private Route Table:
aws-target-vpc that’s associated with private subnetsaws-target-rtb-private1-ap-south-1a10.0.0.0/16on-prem-to-target-peeringaws-target-rtb-private2-ap-south-1b)aws-target-rtb-private3-ap-south-1a)aws-target-rtb-private4-ap-south-1b)ssh -i on-prem-web-key.pem ec2-user@SOURCE_PUBLIC_IP
We’ll test if we can reach the target VPC’s IP range:
# Ping is typically blocked, but we can test with telnet or nc
# Since there's nothing running in target VPC yet, we'll verify routing
ip route get 10.1.1.1
# Expected output should show the route exists
# Output: 10.1.1.1 via <peering-connection> dev eth0 src 10.0.1.x
Ensure you can still connect to your source database:
psql --host=YOUR_SOURCE_RDS_ENDPOINT --port=5432 --username=postgres --dbname=legacydb -c "SELECT COUNT(*) FROM users;"
Expected output: 4
Click Subnet groups in the left sidebar
Click Create DB subnet group
Configure:
target-production-db-subnet-groupSubnet group for production Multi-AZ databaseaws-target-vpcAdd subnets:
10.1.21.0/24 (target-db-subnet-1a)10.1.22.0/24 (target-db-subnet-1b)Click Create
📝 Note the Subnet Group Name: target-production-db-subnet-group
This is the production-grade database that will receive the migrated data.
production-dbpostgresProductionDB2024!)aws-target-vpctarget-production-db-subnet-grouptarget-db-sgClick Additional configuration to expand:
productiondb ⚠️ Important!Backup:
Encryption:
Log exports: (optional, but recommended)
Maintenance:
Deletion protection:
Once available:
production-dbproduction-db.xxxxxxxx.ap-south-1.rds.amazonaws.comNow let’s verify that DMS will be able to connect to both databases through VPC peering.
ssh -i on-prem-web-key.pem ec2-user@SOURCE_PUBLIC_IP
Try connecting to the production database from the source web server:
psql --host=YOUR_TARGET_RDS_ENDPOINT --port=5432 --username=postgres --dbname=productiondb
Enter the production database password when prompted.
Expected result: You should successfully connect and see:
psql (16.x)
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Type "help" for help.
productiondb=>
-- List all tables (should be empty)
\dt
-- Exit
\q
Expected output: Did not find any relations. (database is empty, ready for migration)
✅ If you can connect, VPC peering is working correctly!
DMS requires its own subnet group to deploy the replication instance.
Click Subnet groups in the left sidebar
Click Create subnet group
Configure:
dms-target-subnet-groupDMS subnet group for replication instanceaws-target-vpcAdd subnets:
10.1.1.0/24 (aws-target-subnet-public1-ap-south-1a)10.1.2.0/24 (aws-target-subnet-public2-ap-south-1b)Click Create subnet group
A bastion host allows you to securely access private resources for testing and troubleshooting.
Go to VPC → Security Groups
Click Create security group
Configure:
target-bastion-sgSecurity group for bastion hostaws-target-vpcInbound rules:
Outbound rules: Leave default
Click Create security group
target-db-sgtarget-bastion-sgtarget-bastion-hoston-prem-web-key)target-bastion-sgBefore proceeding to Phase 3 and 4, verify everything:
aws-target-vpc created with CIDR 10.1.0.0/16target-web-sg created and configuredtarget-db-sg created and configuredtarget-dms-sg createdon-prem-db-sg updated for peeringtarget-bastion-sg created (if using bastion)production-db is available📝 Save these values for Phase 3 (MGN) and Phase 4 (DMS):
| Resource | Value | Notes |
|---|---|---|
| VPC ID | vpc-xxxxxxxx | aws-target-vpc |
| Public Subnet 1a | subnet-xxxxxxxx | 10.1.1.0/24 |
| Public Subnet 1b | subnet-xxxxxxxx | 10.1.2.0/24 |
| App Private Subnet 1a | subnet-xxxxxxxx | 10.1.11.0/24 - For MGN |
| App Private Subnet 1b | subnet-xxxxxxxx | 10.1.12.0/24 |
| DB Private Subnet 1a | subnet-xxxxxxxx | 10.1.21.0/24 |
| DB Private Subnet 1b | subnet-xxxxxxxx | 10.1.22.0/24 |
| Web SG ID | sg-xxxxxxxx | target-web-sg |
| DB SG ID | sg-xxxxxxxx | target-db-sg |
| DMS SG ID | sg-xxxxxxxx | target-dms-sg |
| Peering Connection ID | pcx-xxxxxxxx | on-prem-to-target |
| Resource | Value | Notes |
|---|---|---|
| RDS Endpoint | production-db.xxx.ap-south-1.rds.amazonaws.com | Target for DMS |
| DB Name | productiondb | Empty database |
| Username | postgres | Master username |
| Password | ****** | Keep secure! |
| Multi-AZ | Yes | Primary: 1a, Standby: 1b |
| Port | 5432 | PostgreSQL port |
| Resource | Value | Notes |
|---|---|---|
| DMS Subnet Group | dms-target-subnet-group | For replication instance |
Solution:
target-db-sg allows PostgreSQL from 10.0.0.0/16on-prem-db-sg allows PostgreSQL from 10.1.0.0/16telnet TARGET_ENDPOINT 5432Solution:
Solution:
Solution:
Run these commands from the source web server to validate the setup:
# Test DNS resolution of target database
nslookup YOUR_TARGET_RDS_ENDPOINT
# Test connectivity to target database
telnet YOUR_TARGET_RDS_ENDPOINT 5432
# Test connectivity to target VPC
ping -c 3 10.1.1.1
# Verify routing
ip route | grep 10.1.0.0
# Test actual database connection
psql --host=YOUR_TARGET_RDS_ENDPOINT --port=5432 --username=postgres --dbname=productiondb -c "\l"
💰 To minimize costs while building this phase:
Stop RDS instances when not actively testing
Skip the bastion host if using Systems Manager
Don’t create NAT Gateway unless absolutely needed
✅ Phase 2 Complete!
You now have a production-grade target environment with:
Ready for migration!
Proceed to:
# SSH to source web server
ssh -i on-prem-web-key.pem ec2-user@SOURCE_PUBLIC_IP
# Connect to source database
psql --host=SOURCE_RDS_ENDPOINT --port=5432 --username=postgres --dbname=legacydb
# Connect to target database
psql --host=TARGET_RDS_ENDPOINT --port=5432 --username=postgres --dbname=productiondb
# Test cross-VPC connectivity
telnet TARGET_RDS_ENDPOINT 5432
# Verify routing
ip route get 10.1.1.1
In this comprehensive guide, we’ve successfully built a production-grade target cloud environment that perfectly prepares us for seamless cloud migration. This foundation is essential for understanding modern cloud architecture patterns and provides the perfect destination for AWS Application Migration Service (MGN) and Database Migration Service (DMS).
What we’ve accomplished:
Key Learning Outcomes:
This environment is now ready for the next phase of our migration journey, where we’ll perform the actual migration using AWS MGN and DMS services.
This is Part 2 of a comprehensive AWS migration series. Here is the Part 3, where we’ll migrate the web server using AWS Application Migration Service (MGN).