On-Premise to AWS Migration Roadmap: Complete Strategy Guide | Forrict Skip to main content
Cloud Migration Strategy

On-Premise to AWS Migration Roadmap: Complete Strategy Guide

Forrict Team
On-Premise to AWS Migration Roadmap: Complete Strategy Guide
Step-by-step migration roadmap using AWS 6R strategies, Migration Hub, DMS, and Application Migration Service for successful cloud transformation

On-Premise to AWS Migration Roadmap: Complete Strategy Guide

Master the AWS migration journey with proven 6R strategies, comprehensive tooling, and real-world implementation patterns for successful cloud transformation

Introduction

Migrating from on-premise infrastructure to AWS is a transformative journey that requires careful planning, proven strategies, and the right tooling. Whether you’re moving a single application or an entire data center, understanding AWS’s migration framework and tools is essential for success.

This comprehensive guide provides a complete roadmap for on-premise to AWS migration, covering the 6R strategies, AWS migration tools, timeline planning, and hands-on implementation examples.

What You’ll Learn:

  • AWS 6R migration strategies (Rehost, Replatform, Refactor, Repurchase, Retire, Retain)
  • Migration phases: Assessment, Planning, Execution, Optimization
  • AWS Migration Hub for centralized tracking
  • Application Discovery Service for inventory management
  • Database Migration Service (DMS) implementation
  • AWS Application Migration Service (MGN) configuration
  • Real-world migration timelines and cost optimization
  • Post-migration optimization strategies

Understanding the 6R Migration Strategies

1. Rehost (Lift-and-Shift)

Description: Move applications to AWS without changes

When to Use:

  • Tight migration deadlines
  • Applications that work well as-is
  • Minimal cloud optimization initially
  • Quick data center exit required

Advantages:

  • Fastest migration approach
  • Lowest initial cost
  • Minimal application risk
  • Skills reuse from on-premise

Disadvantages:

  • Misses cloud-native benefits
  • May have higher long-term costs
  • Limited scalability improvements
  • Technical debt carried forward

Tools:

  • AWS Application Migration Service (MGN)
  • CloudEndure Migration
  • VM Import/Export

Example Use Cases:

  • Legacy ERP systems
  • Monolithic applications
  • Compliance-driven migrations
  • POC/Pilot migrations

2. Replatform (Lift-Tinker-and-Shift)

Description: Make minor cloud optimizations without changing core architecture

When to Use:

  • Want some cloud benefits without major changes
  • Database can benefit from managed services
  • Load balancing and autoscaling needed
  • Moderate timeline available

Advantages:

  • Some cloud optimization benefits
  • Moderate migration speed
  • Reduced operational overhead
  • No major code changes needed

Disadvantages:

  • Limited optimization compared to refactoring
  • May still have architectural limitations
  • Partial cloud-native adoption

Typical Changes:

  • On-premise MySQL → Amazon RDS
  • Self-managed Redis → Amazon ElastiCache
  • Manual load balancing → Elastic Load Balancing
  • Fixed capacity → Auto Scaling Groups

Example Use Cases:

  • Web applications with databases
  • Applications with stateful components
  • Services needing high availability

3. Refactor/Re-architect

Description: Reimagine application architecture using cloud-native features

When to Use:

  • Maximize cloud benefits
  • Scalability requirements significant
  • Microservices architecture desired
  • Long-term cloud strategy

Advantages:

  • Maximum cloud optimization
  • Best scalability and resilience
  • Reduced operational costs
  • Modern architecture patterns

Disadvantages:

  • Highest migration cost
  • Longest timeline
  • Requires significant development
  • Business logic may need changes

Common Patterns:

  • Monolith → Microservices (ECS/EKS)
  • Server-based → Serverless (Lambda)
  • Traditional queue → SQS/SNS
  • Session state → DynamoDB/ElastiCache

Example Use Cases:

  • SaaS applications
  • High-traffic web applications
  • Data-intensive workloads
  • Greenfield development

4. Repurchase (Drop-and-Shop)

Description: Replace existing application with cloud-based SaaS alternative

When to Use:

  • SaaS alternative available
  • License costs too high
  • Maintenance burden significant
  • Standard functionality sufficient

Advantages:

  • No infrastructure management
  • Predictable costs
  • Regular updates/features
  • Reduced operational burden

Disadvantages:

  • Data migration complexity
  • Vendor lock-in
  • Customization limitations
  • Training requirements

Common Transitions:

  • On-premise email → Microsoft 365/Google Workspace
  • Self-hosted CRM → Salesforce
  • Custom HR system → Workday
  • File servers → Dropbox/Box

5. Retire

Description: Decommission applications no longer needed

When to Use:

  • Low/no usage
  • Duplicate functionality
  • EOL applications
  • Cost reduction focus

Advantages:

  • Immediate cost savings
  • Reduced complexity
  • Lower security risk
  • Simplified portfolio

Process:

  • Identify candidates via Discovery Service
  • Validate with stakeholders
  • Archive data if required
  • Decommission infrastructure

Cost Impact:

  • Average 10-20% of portfolio can be retired
  • Significant TCO reduction
  • Reduced migration scope

6. Retain (Revisit)

Description: Keep applications on-premise for now

When to Use:

  • Recently upgraded systems
  • Compliance constraints
  • Complex dependencies
  • High migration risk

Considerations:

  • Plan for future migration
  • Document retention reasons
  • Regular reassessment
  • Hybrid connectivity may be needed

Migration Phases and Timeline

Phase 1: Assessment (2-4 weeks)

Objectives:

  • Discover all applications and dependencies
  • Classify workloads by 6R strategy
  • Estimate costs and resources
  • Identify risks and constraints

Activities:

# scripts/assessment_automation.py
import boto3
import json
from typing import Dict, List
from datetime import datetime

class MigrationAssessment:
    """Automated migration assessment using AWS Discovery Service"""

    def __init__(self, region: str = 'eu-central-1'):
        self.discovery = boto3.client('discovery', region_name=region)
        self.pricing = boto3.client('pricing', region_name='us-east-1')
        self.ce = boto3.client('ce', region_name=region)

    def discover_on_premise_inventory(self) -> Dict:
        """
        Discover on-premise infrastructure using Discovery Agent

        Returns inventory of servers, applications, and dependencies
        """
        # Start discovery agents (deployed on-premise)
        agents = self.discovery.describe_agents()

        inventory = {
            'servers': [],
            'applications': [],
            'dependencies': [],
            'timestamp': datetime.utcnow().isoformat()
        }

        # List discovered servers
        servers = self.discovery.describe_configurations(
            configurationType='SERVER'
        )

        for server in servers.get('configurations', []):
            inventory['servers'].append({
                'id': server.get('server.configurationId'),
                'hostname': server.get('server.hostName'),
                'os': server.get('server.osName'),
                'cpu_count': server.get('server.cpuCount'),
                'memory_mb': server.get('server.totalRamInMB'),
                'network_interfaces': server.get('server.networkInterfaceCount'),
            })

        # Map applications
        applications = self.discovery.list_applications()

        for app in applications.get('applications', []):
            app_id = app['id']

            # Get application dependencies
            dependencies = self.discovery.describe_configurations(
                configurationType='SERVER',
                filters=[{
                    'name': 'applicationConfigurationId',
                    'values': [app_id],
                    'condition': 'EQUALS'
                }]
            )

            inventory['applications'].append({
                'id': app_id,
                'name': app.get('name'),
                'description': app.get('description'),
                'server_count': len(dependencies.get('configurations', []))
            })

        return inventory

    def calculate_aws_costs(
        self,
        server_specs: List[Dict]
    ) -> Dict:
        """
        Estimate AWS costs for discovered servers

        Uses AWS Pricing API to calculate EC2, storage, and transfer costs
        """
        total_monthly_cost = 0
        cost_breakdown = []

        for server in server_specs:
            # Determine appropriate EC2 instance type
            instance_type = self._recommend_instance_type(
                cpu_count=server['cpu_count'],
                memory_mb=server['memory_mb']
            )

            # Get EC2 pricing
            ec2_cost = self._get_ec2_pricing(instance_type, 'eu-central-1')

            # Estimate EBS storage cost (100GB default)
            storage_cost = self._get_ebs_pricing(100, 'eu-central-1')

            server_monthly_cost = ec2_cost + storage_cost
            total_monthly_cost += server_monthly_cost

            cost_breakdown.append({
                'server': server['hostname'],
                'recommended_instance': instance_type,
                'monthly_cost': round(server_monthly_cost, 2)
            })

        return {
            'total_monthly_cost': round(total_monthly_cost, 2),
            'cost_breakdown': cost_breakdown,
            'assumptions': {
                'region': 'eu-central-1',
                'storage_per_server_gb': 100,
                'monthly_hours': 730,
                'os': 'Linux'
            }
        }

    def _recommend_instance_type(
        self,
        cpu_count: int,
        memory_mb: int
    ) -> str:
        """Recommend EC2 instance type based on specs"""

        memory_gb = memory_mb / 1024

        # Simple instance type mapping
        if cpu_count <= 2 and memory_gb <= 4:
            return 't3.medium'
        elif cpu_count <= 2 and memory_gb <= 8:
            return 't3.large'
        elif cpu_count <= 4 and memory_gb <= 16:
            return 'm6i.xlarge'
        elif cpu_count <= 8 and memory_gb <= 32:
            return 'm6i.2xlarge'
        elif cpu_count <= 16 and memory_gb <= 64:
            return 'm6i.4xlarge'
        else:
            return 'm6i.8xlarge'

    def _get_ec2_pricing(self, instance_type: str, region: str) -> float:
        """Get EC2 on-demand pricing"""
        try:
            response = self.pricing.get_products(
                ServiceCode='AmazonEC2',
                Filters=[
                    {'Type': 'TERM_MATCH', 'Field': 'instanceType', 'Value': instance_type},
                    {'Type': 'TERM_MATCH', 'Field': 'location', 'Value': 'EU (Frankfurt)'},
                    {'Type': 'TERM_MATCH', 'Field': 'operatingSystem', 'Value': 'Linux'},
                    {'Type': 'TERM_MATCH', 'Field': 'tenancy', 'Value': 'Shared'},
                    {'Type': 'TERM_MATCH', 'Field': 'preInstalledSw', 'Value': 'NA'},
                ]
            )

            price_list = json.loads(response['PriceList'][0])
            on_demand = price_list['terms']['OnDemand']
            price_dimensions = list(on_demand.values())[0]['priceDimensions']
            hourly_price = float(list(price_dimensions.values())[0]['pricePerUnit']['USD'])

            return hourly_price * 730  # Monthly cost

        except Exception as e:
            print(f"Pricing error for {instance_type}: {str(e)}")
            return 100  # Default estimate

    def _get_ebs_pricing(self, size_gb: int, region: str) -> float:
        """Get EBS GP3 pricing"""
        # GP3 pricing: ~$0.08/GB-month in eu-central-1
        return size_gb * 0.08

    def classify_workloads(
        self,
        inventory: Dict
    ) -> Dict:
        """
        Classify applications by 6R strategy

        Uses heuristics based on application characteristics
        """
        classifications = {
            'rehost': [],
            'replatform': [],
            'refactor': [],
            'repurchase': [],
            'retire': [],
            'retain': []
        }

        for app in inventory['applications']:
            # Simple classification logic (in reality, this requires analysis)
            server_count = app['server_count']
            app_name = app['name'].lower()

            if 'legacy' in app_name or 'old' in app_name:
                if server_count == 0:
                    classifications['retire'].append(app)
                else:
                    classifications['rehost'].append(app)

            elif 'database' in app_name or 'db' in app_name:
                classifications['replatform'].append(app)

            elif 'web' in app_name or 'api' in app_name:
                if server_count > 5:
                    classifications['refactor'].append(app)
                else:
                    classifications['replatform'].append(app)

            elif 'sap' in app_name or 'oracle' in app_name:
                classifications['retain'].append(app)

            else:
                classifications['rehost'].append(app)

        return classifications

    def generate_assessment_report(
        self,
        output_file: str = 'migration_assessment.json'
    ) -> Dict:
        """Generate comprehensive migration assessment report"""

        print("Discovering on-premise inventory...")
        inventory = self.discover_on_premise_inventory()

        print("Calculating AWS costs...")
        costs = self.calculate_aws_costs(inventory['servers'])

        print("Classifying workloads...")
        classifications = self.classify_workloads(inventory)

        report = {
            'assessment_date': datetime.utcnow().isoformat(),
            'inventory_summary': {
                'total_servers': len(inventory['servers']),
                'total_applications': len(inventory['applications']),
            },
            'cost_analysis': costs,
            'migration_strategy': {
                'rehost_count': len(classifications['rehost']),
                'replatform_count': len(classifications['replatform']),
                'refactor_count': len(classifications['refactor']),
                'repurchase_count': len(classifications['repurchase']),
                'retire_count': len(classifications['retire']),
                'retain_count': len(classifications['retain']),
            },
            'classifications': classifications,
            'recommendations': self._generate_recommendations(classifications, costs)
        }

        with open(output_file, 'w') as f:
            json.dump(report, f, indent=2, default=str)

        print(f"\nAssessment report saved to {output_file}")
        return report

    def _generate_recommendations(
        self,
        classifications: Dict,
        costs: Dict
    ) -> List[str]:
        """Generate migration recommendations"""

        recommendations = []

        # Retire recommendations
        if classifications['retire']:
            recommendations.append(
                f"Immediately retire {len(classifications['retire'])} unused applications "
                f"to reduce migration scope and save costs"
            )

        # Rehost quick wins
        if classifications['rehost']:
            recommendations.append(
                f"Start with rehosting {len(classifications['rehost'])} applications "
                f"using AWS MGN for quick migration wins"
            )

        # Database replatform
        if classifications['replatform']:
            recommendations.append(
                f"Replatform {len(classifications['replatform'])} database workloads "
                f"to Amazon RDS/Aurora for reduced operational overhead"
            )

        # Cost optimization
        total_cost = costs['total_monthly_cost']
        if total_cost > 10000:
            potential_savings = total_cost * 0.3  # 30% savings with Reserved Instances
            recommendations.append(
                f"Consider Reserved Instances for {potential_savings:.2f} USD/month savings "
                f"(~30% of {total_cost:.2f} USD/month)"
            )

        return recommendations

# Example usage
if __name__ == '__main__':
    assessor = MigrationAssessment(region='eu-central-1')

    # Generate assessment
    report = assessor.generate_assessment_report()

    print("\n=== Migration Assessment Summary ===")
    print(f"Total servers discovered: {report['inventory_summary']['total_servers']}")
    print(f"Total applications: {report['inventory_summary']['total_applications']}")
    print(f"\nEstimated monthly AWS cost: ${report['cost_analysis']['total_monthly_cost']}")
    print(f"\n6R Strategy Distribution:")
    for strategy, count in report['migration_strategy'].items():
        print(f"  {strategy}: {count}")
    print(f"\nRecommendations:")
    for i, rec in enumerate(report['recommendations'], 1):
        print(f"  {i}. {rec}")

Phase 2: Planning (4-6 weeks)

Objectives:

  • Design target AWS architecture
  • Create detailed migration plan
  • Set up AWS landing zone
  • Establish migration tooling

AWS Migration Hub Setup:

// lib/migration-hub-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as s3 from 'aws-cdk-lib/aws-s3';
import { Construct } from 'constructs';

export class MigrationHubStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // S3 bucket for migration artifacts
    const migrationBucket = new s3.Bucket(this, 'MigrationArtifacts', {
      bucketName: `migration-artifacts-${this.account}`,
      versioned: true,
      encryption: s3.BucketEncryption.S3_MANAGED,
      lifecycleRules: [
        {
          id: 'DeleteOldArtifacts',
          expiration: cdk.Duration.days(90),
        },
      ],
      removalPolicy: cdk.RemovalPolicy.RETAIN,
    });

    // IAM role for Migration Hub
    const migrationHubRole = new iam.Role(this, 'MigrationHubRole', {
      assumedBy: new iam.ServicePrincipal('migrationhub.amazonaws.com'),
      description: 'Role for AWS Migration Hub',
      managedPolicies: [
        iam.ManagedPolicy.fromAwsManagedPolicyName('AWSMigrationHubFullAccess'),
        iam.ManagedPolicy.fromAwsManagedPolicyName('ApplicationDiscoveryServiceFullAccess'),
      ],
    });

    // IAM role for Application Migration Service (MGN)
    const mgnRole = new iam.Role(this, 'MGNRole', {
      assumedBy: new iam.ServicePrincipal('mgn.amazonaws.com'),
      description: 'Role for AWS Application Migration Service',
      inlinePolicies: {
        MGNPolicy: new iam.PolicyDocument({
          statements: [
            new iam.PolicyStatement({
              effect: iam.Effect.ALLOW,
              actions: [
                'ec2:DescribeInstances',
                'ec2:DescribeSnapshots',
                'ec2:DescribeVolumes',
                'ec2:CreateSnapshot',
                'ec2:CreateTags',
                'ec2:CreateVolume',
                'ec2:DeleteSnapshot',
                'ec2:DeleteVolume',
                'ec2:DescribeImages',
                'ec2:DescribeInstanceAttribute',
                'ec2:DescribeInstanceStatus',
                'ec2:DescribeSecurityGroups',
                'ec2:DescribeSubnets',
                'ec2:DescribeVpcs',
                'ec2:ModifyInstanceAttribute',
                'ec2:RunInstances',
                'ec2:StartInstances',
                'ec2:StopInstances',
                'ec2:TerminateInstances',
              ],
              resources: ['*'],
            }),
            new iam.PolicyStatement({
              effect: iam.Effect.ALLOW,
              actions: [
                's3:GetObject',
                's3:PutObject',
                's3:DeleteObject',
              ],
              resources: [migrationBucket.arnForObjects('*')],
            }),
          ],
        }),
      },
    });

    // Output
    new cdk.CfnOutput(this, 'MigrationBucketName', {
      value: migrationBucket.bucketName,
      description: 'S3 bucket for migration artifacts',
    });

    new cdk.CfnOutput(this, 'MigrationHubRoleArn', {
      value: migrationHubRole.roleArn,
      description: 'IAM role for Migration Hub',
    });

    new cdk.CfnOutput(this, 'MGNRoleArn', {
      value: mgnRole.roleArn,
      description: 'IAM role for Application Migration Service',
    });
  }
}

Phase 3: Execution (8-12 weeks)

Database Migration with DMS:

// lib/dms-migration-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as dms from 'aws-cdk-lib/aws-dms';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as rds from 'aws-cdk-lib/aws-rds';
import * as iam from 'aws-cdk-lib/aws-iam';
import { Construct } from 'constructs';

export class DMSMigrationStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const vpc = ec2.Vpc.fromLookup(this, 'VPC', {
      isDefault: false,
    });

    // DMS Subnet Group
    const dmsSubnetGroup = new dms.CfnReplicationSubnetGroup(
      this,
      'DMSSubnetGroup',
      {
        replicationSubnetGroupDescription: 'Subnet group for DMS replication',
        subnetIds: vpc.privateSubnets.map(subnet => subnet.subnetId),
        replicationSubnetGroupIdentifier: 'dms-subnet-group',
      }
    );

    // DMS Replication Instance
    const replicationInstance = new dms.CfnReplicationInstance(
      this,
      'DMSReplicationInstance',
      {
        replicationInstanceClass: 'dms.c5.xlarge',
        replicationInstanceIdentifier: 'dms-replication-instance',
        allocatedStorage: 100,
        multiAz: true,
        engineVersion: '3.5.1',
        replicationSubnetGroupIdentifier: dmsSubnetGroup.replicationSubnetGroupIdentifier,
        publiclyAccessible: false,
      }
    );

    replicationInstance.addDependency(dmsSubnetGroup);

    // Source Endpoint (On-premise MySQL)
    const sourceEndpoint = new dms.CfnEndpoint(this, 'SourceEndpoint', {
      endpointType: 'source',
      engineName: 'mysql',
      endpointIdentifier: 'source-mysql-on-premise',
      serverName: 'on-premise-mysql.company.local',  // On-premise server
      port: 3306,
      username: 'dms_user',
      password: 'secure_password',  // Should use Secrets Manager
      databaseName: 'production_db',
      sslMode: 'require',
    });

    // Target Endpoint (Amazon RDS)
    const targetEndpoint = new dms.CfnEndpoint(this, 'TargetEndpoint', {
      endpointType: 'target',
      engineName: 'aurora-postgresql',
      endpointIdentifier: 'target-rds-aurora',
      serverName: 'aurora-cluster.xxxxx.eu-central-1.rds.amazonaws.com',
      port: 5432,
      username: 'admin',
      password: 'secure_password',  // Should use Secrets Manager
      databaseName: 'migrated_db',
      sslMode: 'require',
    });

    // DMS Migration Task
    const migrationTask = new dms.CfnReplicationTask(
      this,
      'MigrationTask',
      {
        replicationTaskIdentifier: 'mysql-to-aurora-migration',
        replicationInstanceArn: replicationInstance.ref,
        sourceEndpointArn: sourceEndpoint.ref,
        targetEndpointArn: targetEndpoint.ref,
        migrationType: 'full-load-and-cdc',  // Full load + change data capture
        tableMappings: JSON.stringify({
          rules: [
            {
              'rule-type': 'selection',
              'rule-id': '1',
              'rule-name': 'include-all-tables',
              'object-locator': {
                'schema-name': '%',
                'table-name': '%',
              },
              'rule-action': 'include',
            },
            {
              'rule-type': 'transformation',
              'rule-id': '2',
              'rule-name': 'convert-to-lowercase',
              'rule-target': 'table',
              'object-locator': {
                'schema-name': '%',
                'table-name': '%',
              },
              'rule-action': 'convert-lowercase',
            },
          ],
        }),
        replicationTaskSettings: JSON.stringify({
          Logging: {
            EnableLogging: true,
            LogComponents: [
              {
                Id: 'SOURCE_CAPTURE',
                Severity: 'LOGGER_SEVERITY_DEFAULT',
              },
              {
                Id: 'TARGET_LOAD',
                Severity: 'LOGGER_SEVERITY_DEFAULT',
              },
            ],
          },
          ControlTablesSettings: {
            ControlSchema: 'dms_control',
            HistoryTimeslotInMinutes: 5,
          },
          FullLoadSettings: {
            TargetTablePrepMode: 'DROP_AND_CREATE',
            MaxFullLoadSubTasks: 8,
          },
          ChangeDataCaptureSettings: {
            StreamBufferSizeInMB: 16,
            StreamBufferCount: 3,
          },
        }),
      }
    );

    migrationTask.addDependency(sourceEndpoint);
    migrationTask.addDependency(targetEndpoint);
    migrationTask.addDependency(replicationInstance);

    // CloudWatch Alarms for DMS
    const replicationLag = new cdk.aws_cloudwatch.Alarm(
      this,
      'ReplicationLagAlarm',
      {
        alarmName: 'dms-replication-lag',
        metric: new cdk.aws_cloudwatch.Metric({
          namespace: 'AWS/DMS',
          metricName: 'CDCLatencySource',
          dimensionsMap: {
            ReplicationInstanceIdentifier: replicationInstance.replicationInstanceIdentifier!,
            ReplicationTaskIdentifier: migrationTask.replicationTaskIdentifier!,
          },
          statistic: 'Average',
          period: cdk.Duration.minutes(5),
        }),
        threshold: 300,  // 5 minutes lag
        evaluationPeriods: 2,
      }
    );

    // Outputs
    new cdk.CfnOutput(this, 'ReplicationInstanceEndpoint', {
      value: replicationInstance.attrReplicationInstancePublicIpAddresses.toString(),
      description: 'DMS Replication Instance endpoint',
    });

    new cdk.CfnOutput(this, 'MigrationTaskArn', {
      value: migrationTask.ref,
      description: 'DMS Migration Task ARN',
    });
  }
}

Server Migration with AWS MGN:

# scripts/mgn_migration.py
import boto3
import time
from typing import Dict, List
from datetime import datetime

class MGNMigration:
    """AWS Application Migration Service (MGN) automation"""

    def __init__(self, region: str = 'eu-central-1'):
        self.mgn = boto3.client('mgn', region_name=region)
        self.ec2 = boto3.client('ec2', region_name=region)
        self.region = region

    def initialize_mgn_service(self) -> Dict:
        """Initialize MGN service in AWS account"""

        try:
            response = self.mgn.initialize_service()
            print("MGN service initialized successfully")
            return response
        except self.mgn.exceptions.AccessDeniedException:
            print("MGN already initialized")
            return {}

    def create_replication_configuration_template(
        self,
        subnet_id: str,
        security_group_ids: List[str],
        staging_disk_type: str = 'GP3'
    ) -> str:
        """Create replication configuration template"""

        template = self.mgn.create_replication_configuration_template(
            associateDefaultSecurityGroup=False,
            bandwidthThrottling=0,  # No throttling
            createPublicIP=False,
            dataPlaneRouting='PRIVATE_IP',
            defaultLargeStagingDiskType=staging_disk_type,
            ebsEncryption='DEFAULT',  # Use default EBS encryption
            replicationServerInstanceType='t3.medium',
            replicationServersSecurityGroupsIDs=security_group_ids,
            stagingAreaSubnetId=subnet_id,
            stagingAreaTags={
                'Purpose': 'MGN-Staging',
                'Environment': 'Migration'
            },
            useDedicatedReplicationServer=False
        )

        template_id = template['replicationConfigurationTemplateID']
        print(f"Created replication template: {template_id}")
        return template_id

    def install_replication_agent(
        self,
        server_ip: str,
        server_username: str,
        server_password: str
    ) -> str:
        """
        Instructions to install MGN replication agent on source server

        Note: This is a guide - actual installation happens on source server
        """

        installation_commands = f"""
# AWS MGN Replication Agent Installation

## For Linux Servers:
wget -O ./aws-replication-installer-init.py https://aws-application-migration-service-{self.region}.s3.{self.region}.amazonaws.com/latest/linux/aws-replication-installer-init.py

sudo python3 aws-replication-installer-init.py --region {self.region} --no-prompt

## For Windows Servers:
# Download installer from:
# https://aws-application-migration-service-{self.region}.s3.{self.region}.amazonaws.com/latest/windows/AwsReplicationWindowsInstaller.exe

# Run installer with:
# AwsReplicationWindowsInstaller.exe --region {self.region} --no-prompt

## Verify installation:
sudo systemctl status aws-replication

## Check replication status in AWS Console:
# Navigate to AWS MGN -> Source servers
        """

        print(installation_commands)
        return installation_commands

    def monitor_source_servers(self) -> List[Dict]:
        """Monitor all source servers being replicated"""

        response = self.mgn.describe_source_servers()

        servers = []
        for server in response.get('items', []):
            server_info = {
                'server_id': server['sourceServerID'],
                'hostname': server.get('sourceProperties', {}).get('identificationHints', {}).get('hostname'),
                'replication_status': server.get('dataReplicationInfo', {}).get('dataReplicationState'),
                'last_seen': server.get('dataReplicationInfo', {}).get('lagDuration'),
                'replicated_disks': len(server.get('dataReplicationInfo', {}).get('replicatedDisks', [])),
            }
            servers.append(server_info)

        return servers

    def launch_test_instance(
        self,
        source_server_id: str
    ) -> str:
        """Launch test instance for migration validation"""

        response = self.mgn.start_test(
            sourceServerIDs=[source_server_id]
        )

        job_id = response['job']['jobID']
        print(f"Test instance launch initiated: {job_id}")

        # Monitor test job
        while True:
            job_status = self.mgn.describe_jobs(
                filters={'jobIDs': [job_id]}
            )

            status = job_status['items'][0]['status']
            print(f"Test launch status: {status}")

            if status == 'COMPLETED':
                print("Test instance launched successfully")
                break
            elif status in ['FAILED', 'PENDING']:
                raise Exception(f"Test launch failed with status: {status}")

            time.sleep(30)

        return job_id

    def cutover_server(
        self,
        source_server_id: str
    ) -> str:
        """Perform final cutover for source server"""

        # Finalize replication
        response = self.mgn.start_cutover(
            sourceServerIDs=[source_server_id]
        )

        job_id = response['job']['jobID']
        print(f"Cutover initiated: {job_id}")

        # Monitor cutover job
        while True:
            job_status = self.mgn.describe_jobs(
                filters={'jobIDs': [job_id]}
            )

            status = job_status['items'][0]['status']
            print(f"Cutover status: {status}")

            if status == 'COMPLETED':
                print("Cutover completed successfully")
                break
            elif status in ['FAILED', 'PENDING']:
                raise Exception(f"Cutover failed with status: {status}")

            time.sleep(30)

        return job_id

    def finalize_cutover(
        self,
        source_server_id: str
    ) -> None:
        """Finalize cutover and mark source server for archival"""

        response = self.mgn.finalize_cutover(
            sourceServerID=source_server_id
        )

        print(f"Cutover finalized for server: {source_server_id}")
        print("Source server marked for archival")

    def generate_migration_report(
        self,
        output_file: str = 'mgn_migration_report.txt'
    ) -> None:
        """Generate migration progress report"""

        servers = self.monitor_source_servers()

        report = []
        report.append("=== AWS MGN Migration Report ===")
        report.append(f"Generated: {datetime.utcnow().isoformat()}\n")
        report.append(f"Total servers: {len(servers)}\n")

        status_counts = {}
        for server in servers:
            status = server['replication_status']
            status_counts[status] = status_counts.get(status, 0) + 1

        report.append("Replication Status Summary:")
        for status, count in status_counts.items():
            report.append(f"  {status}: {count} servers")

        report.append("\nServer Details:")
        for server in servers:
            report.append(f"\nServer: {server['hostname']}")
            report.append(f"  ID: {server['server_id']}")
            report.append(f"  Replication Status: {server['replication_status']}")
            report.append(f"  Replicated Disks: {server['replicated_disks']}")

        report_text = '\n'.join(report)

        with open(output_file, 'w') as f:
            f.write(report_text)

        print(f"\nMigration report saved to {output_file}")
        print(report_text)

# Example usage
if __name__ == '__main__':
    mgn = MGNMigration(region='eu-central-1')

    # Initialize MGN
    mgn.initialize_mgn_service()

    # Create replication template
    template_id = mgn.create_replication_configuration_template(
        subnet_id='subnet-xxxxx',
        security_group_ids=['sg-xxxxx']
    )

    # Print agent installation instructions
    mgn.install_replication_agent(
        server_ip='10.0.1.100',
        server_username='admin',
        server_password='password'
    )

    # Monitor servers
    print("\n=== Source Servers ===")
    servers = mgn.monitor_source_servers()
    for server in servers:
        print(f"{server['hostname']}: {server['replication_status']}")

    # Generate report
    mgn.generate_migration_report()

Phase 4: Optimization (Ongoing)

Post-Migration Cost Optimization:

  • Right-size instances based on actual usage
  • Implement Reserved Instances/Savings Plans
  • Enable Auto Scaling where appropriate
  • Implement S3 Intelligent-Tiering
  • Use AWS Compute Optimizer recommendations

Performance Optimization:

  • Implement CloudFront for content delivery
  • Enable RDS Performance Insights
  • Use ElastiCache for frequently accessed data
  • Implement proper monitoring with CloudWatch

Migration Timeline

Week 1-4: Assessment
├── Application discovery
├── Dependency mapping
├── Cost analysis
└── Strategy definition

Week 5-10: Planning
├── AWS account setup
├── Network design (VPC, subnets, connectivity)
├── Security configuration (IAM, KMS, security groups)
├── Migration tools setup (Migration Hub, DMS, MGN)
├── Pilot application selection
└── Detailed migration plan

Week 11-22: Execution
├── Wave 1: Pilot applications (2 weeks)
│   ├── Rehost 2-3 simple applications
│   ├── Validate process
│   └── Lessons learned
├── Wave 2: Databases (3 weeks)
│   ├── DMS setup and testing
│   ├── Full load migration
│   └── CDC replication
├── Wave 3: Core applications (4 weeks)
│   ├── MGN replication
│   ├── Testing instances
│   └── Cutover execution
└── Wave 4: Remaining workloads (3 weeks)
    ├── Batch migrations
    └── Final cutover

Week 23+: Optimization
├── Cost optimization
├── Performance tuning
├── Security hardening
└── Operational excellence

Best Practices

1. Planning

  • Start with comprehensive discovery
  • Create detailed dependency maps
  • Define clear success criteria
  • Plan for rollback scenarios
  • Communicate extensively with stakeholders

2. Execution

  • Migrate in waves, not big bang
  • Test thoroughly before cutover
  • Use automation wherever possible
  • Maintain detailed runbooks
  • Have rollback procedures ready

3. Validation

  • Verify data integrity post-migration
  • Perform load testing
  • Validate all integrations
  • Check security configurations
  • Review cost against estimates

4. Optimization

  • Continuous cost monitoring
  • Regular performance reviews
  • Implement AWS best practices
  • Leverage managed services
  • Automate operational tasks

Conclusion

Successful on-premise to AWS migration requires a structured approach, proven strategies, and the right tools. By following the 6R framework, leveraging AWS migration services, and executing in carefully planned phases, organizations can achieve successful cloud transformations with minimal risk and maximum benefit.

Key Takeaways:

  • Use 6R strategies to classify workloads appropriately
  • Start with comprehensive discovery and assessment
  • Leverage AWS Migration Hub for centralized tracking
  • Use DMS for database migrations with minimal downtime
  • Deploy MGN for server rehosting migrations
  • Execute in waves with thorough testing
  • Optimize continuously post-migration

Ready to start your AWS migration journey? Forrict provides end-to-end migration services, from initial assessment to post-migration optimization, ensuring successful cloud transformation for Dutch businesses.

Resources

F

Forrict Team

AWS expert and consultant at Forrict, specializing in cloud architecture and AWS best practices for Dutch businesses.

Tags

AWS Migration Cloud Migration AWS MGN DMS Migration Hub Strategy 6R Rehosting Refactoring

Related Articles

Ready to Transform Your AWS Infrastructure?

Let's discuss how we can help optimize your cloud journey