Alin Balan
Alin Balan

Software Developer

Verified Expert in Engineering

Full Stack Developer

CTO

Magento Expert

Infrastructure Architect

Blog Post

Deploying Magento 2 on AWS CloudFormation: Complete Infrastructure Guide

January 12, 2026 Infrastructure Learning Technology
Deploying Magento 2 on AWS CloudFormation: Complete Infrastructure Guide

As an AWS CloudFormation expert and Magento specialist, I’m often asked: “How do I deploy Magento 2 on AWS using Infrastructure as Code?” This is an excellent question because CloudFormation allows you to define your entire infrastructure in templates, making deployments repeatable, scalable, and maintainable.

In this comprehensive guide, we’ll walk through deploying a production-ready Magento 2 instance on AWS using CloudFormation templates. We’ll use AWS-native tools and services to create a scalable, high-availability architecture.

What We’re Building

By the end of this tutorial, you’ll have:

  • EC2 Instance: Running Magento 2 with Apache/PHP
  • RDS Database: Managed MySQL/MariaDB for Magento data
  • ElastiCache: Redis cluster for caching and sessions
  • S3 Bucket: For media storage and backups
  • Application Load Balancer: For high availability
  • Auto Scaling Group: To handle traffic spikes
  • CloudFront CDN: For global content delivery
  • VPC: Secure network infrastructure
  • Security Groups: Proper network security

Prerequisites

Before we start, ensure you have:

  • An AWS account with appropriate permissions
  • AWS CLI installed and configured (see our AWS CLI guide)
  • Basic understanding of CloudFormation
  • Magento Marketplace access keys
  • Basic knowledge of YAML/JSON

Understanding AWS Services for Magento

Let’s understand what each AWS service does in our Magento architecture:

EC2: Virtual servers running your Magento application RDS: Managed database service (MySQL/MariaDB) ElastiCache: Managed Redis for caching S3: Object storage for media files and backups ALB: Distributes traffic across multiple EC2 instances Auto Scaling: Automatically adds/removes servers based on demand CloudFront: CDN for fast global content delivery VPC: Isolated network environment Security Groups: Virtual firewalls

Step 1: Prepare Your Environment

Install and Configure AWS CLI

First, ensure AWS CLI is installed and configured:

# Verify AWS CLI installation
aws --version

# Configure AWS credentials
aws configure

# You'll be prompted for:
# - AWS Access Key ID
# - AWS Secret Access Key
# - Default region (e.g., us-east-1)
# - Default output format (json)

Verify AWS Access

Test your AWS access:

# List your S3 buckets
aws s3 ls

# Check your IAM identity
aws sts get-caller-identity

Get Magento Access Keys

You’ll need Magento Marketplace access keys:

  1. Go to Magento Marketplace
  2. Log in or create an account
  3. Navigate to “My Access Keys”
  4. Create a new access key pair
  5. Save the public and private keys securely

Step 2: Create S3 Bucket for CloudFormation Templates

We’ll store our CloudFormation templates in S3 for better organization:

# Create S3 bucket for templates
aws s3 mb s3://magento-cloudformation-templates-$(date +%s)

# Note: Bucket names must be globally unique
# Replace with your unique bucket name

Step 3: Create the Main CloudFormation Template

Let’s create our main CloudFormation template. This will define all our infrastructure:

AWSTemplateFormatVersion: '2010-09-09'
Description: 'Magento 2 on AWS - Production Ready Infrastructure'

Parameters:
  EnvironmentName:
    Type: String
    Default: magento-prod
    Description: Environment name for resource naming
  
  InstanceType:
    Type: String
    Default: t3.large
    AllowedValues:
      - t3.medium
      - t3.large
      - t3.xlarge
      - m5.large
      - m5.xlarge
    Description: EC2 instance type for Magento
  
  DBInstanceClass:
    Type: String
    Default: db.t3.medium
    Description: RDS instance class
  
  DBName:
    Type: String
    Default: magento2
    Description: Database name
  
  DBUsername:
    Type: String
    Default: magento_admin
    NoEcho: true
    Description: Database master username
  
  DBPassword:
    Type: String
    NoEcho: true
    MinLength: 8
    Description: Database master password
  
  MagentoPublicKey:
    Type: String
    Description: Magento Marketplace Public Key
    NoEcho: true
  
  MagentoPrivateKey:
    Type: String
    Description: Magento Marketplace Private Key
    NoEcho: true
  
  AdminEmail:
    Type: String
    Default: [email protected]
    Description: Magento admin email
  
  AdminPassword:
    Type: String
    NoEcho: true
    MinLength: 8
    Description: Magento admin password
  
  KeyPairName:
    Type: AWS::EC2::KeyPair::KeyName
    Description: EC2 Key Pair for SSH access

Resources:
  # VPC and Networking
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-VPC'
  
  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [0, !GetAZs '']
      CidrBlock: 10.0.1.0/24
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-PublicSubnet1'
  
  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [1, !GetAZs '']
      CidrBlock: 10.0.2.0/24
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-PublicSubnet2'
  
  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [0, !GetAZs '']
      CidrBlock: 10.0.3.0/24
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-PrivateSubnet1'
  
  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      AvailabilityZone: !Select [1, !GetAZs '']
      CidrBlock: 10.0.4.0/24
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-PrivateSubnet2'
  
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-IGW'
  
  InternetGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC
  
  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-PublicRT'
  
  DefaultPublicRoute:
    Type: AWS::EC2::Route
    DependsOn: InternetGatewayAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  
  PublicSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet1
  
  PublicSubnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet2
  
  # NAT Gateway for private subnet internet access
  NATGatewayEIP:
    Type: AWS::EC2::EIP
    DependsOn: InternetGatewayAttachment
    Properties:
      Domain: vpc
  
  NATGateway:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt NATGatewayEIP.AllocationId
      SubnetId: !Ref PublicSubnet1
  
  PrivateRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-PrivateRT'
  
  DefaultPrivateRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NATGateway
  
  PrivateSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      SubnetId: !Ref PrivateSubnet1
  
  PrivateSubnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref PrivateRouteTable
      SubnetId: !Ref PrivateSubnet2
  
  # Security Groups
  WebServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub '${EnvironmentName}-WebServerSG'
      GroupDescription: Security group for Magento web servers
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
          Description: HTTP access
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
          Description: HTTPS access
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
          Description: SSH access
      SecurityGroupEgress:
        - IpProtocol: -1
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-WebServerSG'
  
  DatabaseSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub '${EnvironmentName}-DatabaseSG'
      GroupDescription: Security group for RDS database
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
          SourceSecurityGroupId: !Ref WebServerSecurityGroup
          Description: MySQL access from web servers
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-DatabaseSG'
  
  ElastiCacheSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub '${EnvironmentName}-ElastiCacheSG'
      GroupDescription: Security group for ElastiCache Redis
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 6379
          ToPort: 6379
          SourceSecurityGroupId: !Ref WebServerSecurityGroup
          Description: Redis access from web servers
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-ElastiCacheSG'
  
  # S3 Bucket for Media Files
  MagentoMediaBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub '${EnvironmentName}-media-${AWS::AccountId}'
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled
      LifecycleConfiguration:
        Rules:
          - Id: DeleteOldVersions
            Status: Enabled
            NoncurrentVersionExpirationInDays: 30
  
  # IAM Role for EC2 Instances
  EC2InstanceRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy
      Policies:
        - PolicyName: S3Access
          PolicyDocument:
            Version: '2012-10-17'
            Statement:
              - Effect: Allow
                Action:
                  - s3:GetObject
                  - s3:PutObject
                  - s3:DeleteObject
                  - s3:ListBucket
                Resource:
                  - !Sub '${MagentoMediaBucket}/*'
                  - !GetAtt MagentoMediaBucket.Arn
  
  EC2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref EC2InstanceRole
  
  # RDS Subnet Group
  DBSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupDescription: Subnet group for RDS database
      SubnetIds:
        - !Ref PrivateSubnet1
        - !Ref PrivateSubnet2
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-DBSubnetGroup'
  
  # RDS Database Instance
  MagentoDatabase:
    Type: AWS::RDS::DBInstance
    Properties:
      DBInstanceIdentifier: !Sub '${EnvironmentName}-database'
      DBName: !Ref DBName
      DBInstanceClass: !Ref DBInstanceClass
      Engine: mariadb
      EngineVersion: '10.11'
      MasterUsername: !Ref DBUsername
      MasterUserPassword: !Ref DBPassword
      AllocatedStorage: 100
      StorageType: gp3
      StorageEncrypted: true
      VPCSecurityGroups:
        - !Ref DatabaseSecurityGroup
      DBSubnetGroupName: !Ref DBSubnetGroup
      BackupRetentionPeriod: 7
      PreferredBackupWindow: '03:00-04:00'
      PreferredMaintenanceWindow: 'sun:04:00-sun:05:00'
      MultiAZ: false
      PubliclyAccessible: false
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-Database'
  
  # ElastiCache Subnet Group
  ElastiCacheSubnetGroup:
    Type: AWS::ElastiCache::SubnetGroup
    Properties:
      Description: Subnet group for ElastiCache
      SubnetIds:
        - !Ref PrivateSubnet1
        - !Ref PrivateSubnet2
  
  # ElastiCache Redis Cluster
  MagentoRedisCache:
    Type: AWS::ElastiCache::ReplicationGroup
    Properties:
      ReplicationGroupId: !Sub '${EnvironmentName}-redis-cache'
      Description: Redis cluster for Magento cache
      Engine: redis
      CacheNodeType: cache.t3.medium
      NumCacheClusters: 2
      AutomaticFailoverEnabled: true
      MultiAZEnabled: true
      CacheSubnetGroupName: !Ref ElastiCacheSubnetGroup
      SecurityGroupIds:
        - !Ref ElastiCacheSecurityGroup
      AtRestEncryptionEnabled: true
      TransitEncryptionEnabled: false
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-RedisCache'
  
  # Application Load Balancer
  ApplicationLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: !Sub '${EnvironmentName}-ALB'
      Type: application
      Scheme: internet-facing
      Subnets:
        - !Ref PublicSubnet1
        - !Ref PublicSubnet2
      SecurityGroups:
        - !Ref WebServerSecurityGroup
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-ALB'
  
  ALBTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub '${EnvironmentName}-TargetGroup'
      Port: 80
      Protocol: HTTP
      VpcId: !Ref VPC
      HealthCheckPath: /health
      HealthCheckIntervalSeconds: 30
      HealthCheckTimeoutSeconds: 5
      HealthyThresholdCount: 2
      UnhealthyThresholdCount: 3
      TargetType: instance
  
  ALBListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref ALBTargetGroup
      LoadBalancerArn: !Ref ApplicationLoadBalancer
      Port: 80
      Protocol: HTTP
  
  # Launch Template for Auto Scaling
  LaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateName: !Sub '${EnvironmentName}-LaunchTemplate'
      LaunchTemplateData:
        ImageId: ami-0c55b159cbfafe1f0  # Amazon Linux 2023 - Update with your region's AMI
        InstanceType: !Ref InstanceType
        KeyName: !Ref KeyPairName
        IamInstanceProfile:
          Arn: !GetAtt EC2InstanceProfile.Arn
        SecurityGroupIds:
          - !Ref WebServerSecurityGroup
        UserData:
          Fn::Base64: !Sub |
            #!/bin/bash
            yum update -y
            yum install -y httpd php php-mysqlnd php-xml php-gd php-mbstring php-curl php-zip php-intl php-bcmath php-soap php-opcache git
            
            # Install Composer
            curl -sS https://getcomposer.org/installer | php
            mv composer.phar /usr/local/bin/composer
            
            # Install Redis PHP extension
            yum install -y php-pecl-redis
            
            # Configure PHP
            sed -i 's/memory_limit = .*/memory_limit = 2G/' /etc/php.ini
            sed -i 's/max_execution_time = .*/max_execution_time = 1800/' /etc/php.ini
            sed -i 's/upload_max_filesize = .*/upload_max_filesize = 64M/' /etc/php.ini
            sed -i 's/post_max_size = .*/post_max_size = 64M/' /etc/php.ini
            
            # Install AWS CLI
            yum install -y aws-cli
            
            # Mount EFS (if using EFS for shared storage)
            # yum install -y amazon-efs-utils
            # mkdir -p /var/www/html
            # mount -t efs ${EFSFileSystemId}:/ /var/www/html
            
            # Download and install Magento
            cd /var/www/html
            composer create-project --repository-url=https://repo.magento.com/ \
              magento/project-community-edition . \
              --username=${MagentoPublicKey} \
              --password=${MagentoPrivateKey}
            
            # Set permissions
            chown -R apache:apache /var/www/html
            find . -type f -exec chmod 644 {} \;
            find . -type d -exec chmod 755 {} \;
            chmod +x bin/magento
            
            # Install Magento
            /usr/bin/php bin/magento setup:install \
              --base-url=http://${ApplicationLoadBalancer.DNSName}/ \
              --base-url-secure=https://${ApplicationLoadBalancer.DNSName}/ \
              --db-host=${MagentoDatabase.Endpoint.Address} \
              --db-name=${DBName} \
              --db-user=${DBUsername} \
              --db-password=${DBPassword} \
              --admin-firstname=Admin \
              --admin-lastname=User \
              --admin-email=${AdminEmail} \
              --admin-user=admin \
              --admin-password=${AdminPassword} \
              --language=en_US \
              --currency=USD \
              --timezone=America/New_York \
              --use-rewrites=1 \
              --backend-frontname=admin \
              --session-save=redis \
              --session-save-redis-host=${MagentoRedisCache.RedisEndpoint.Address} \
              --session-save-redis-port=6379 \
              --session-save-redis-db=0 \
              --cache-backend=redis \
              --cache-backend-redis-server=${MagentoRedisCache.RedisEndpoint.Address} \
              --cache-backend-redis-port=6379 \
              --cache-backend-redis-db=1 \
              --page-cache=redis \
              --page-cache-redis-server=${MagentoRedisCache.RedisEndpoint.Address} \
              --page-cache-redis-port=6379 \
              --page-cache-redis-db=2
            
            # Configure Apache
            echo '<VirtualHost *:80>
              ServerName ${ApplicationLoadBalancer.DNSName}
              DocumentRoot /var/www/html/pub
              
              <Directory /var/www/html/pub>
                Options Indexes FollowSymLinks
                AllowOverride All
                Require all granted
              </Directory>
              
              ErrorLog /var/log/httpd/magento_error.log
              CustomLog /var/log/httpd/magento_access.log combined
            </VirtualHost>' > /etc/httpd/conf.d/magento.conf
            
            # Enable Apache modules
            systemctl enable httpd
            systemctl start httpd
            
            # Set production mode
            /usr/bin/php bin/magento deploy:mode:set production
            /usr/bin/php bin/magento setup:static-content:deploy -f
            /usr/bin/php bin/magento indexer:reindex
            
            # Setup cron
            echo '* * * * * /usr/bin/php /var/www/html/bin/magento cron:run' | crontab -u apache -
  
  # Auto Scaling Group
  AutoScalingGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      AutoScalingGroupName: !Sub '${EnvironmentName}-ASG'
      VPCZoneIdentifier:
        - !Ref PrivateSubnet1
        - !Ref PrivateSubnet2
      LaunchTemplate:
        LaunchTemplateId: !Ref LaunchTemplate
        Version: !GetAtt LaunchTemplate.LatestVersionNumber
      MinSize: 1
      MaxSize: 4
      DesiredCapacity: 2
      TargetGroupARNs:
        - !Ref ALBTargetGroup
      HealthCheckType: ELB
      HealthCheckGracePeriod: 300
      Tags:
        - Key: Name
          Value: !Sub '${EnvironmentName}-Instance'
          PropagateAtLaunch: true
  
  # Auto Scaling Policies
  ScaleUpPolicy:
    Type: AWS::AutoScaling::ScalingPolicy
    Properties:
      AdjustmentType: ChangeInCapacity
      AutoScalingGroupName: !Ref AutoScalingGroup
      Cooldown: 300
      ScalingAdjustment: 1
  
  ScaleDownPolicy:
    Type: AWS::AutoScaling::ScalingPolicy
    Properties:
      AdjustmentType: ChangeInCapacity
      AutoScalingGroupName: !Ref AutoScalingGroup
      Cooldown: 300
      ScalingAdjustment: -1
  
  # CloudWatch Alarms for Auto Scaling
  CPUAlarmHigh:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: !Sub '${EnvironmentName}-CPU-High'
      AlarmDescription: Alarm when CPU exceeds 70%
      MetricName: CPUUtilization
      Namespace: AWS/EC2
      Statistic: Average
      Period: 300
      EvaluationPeriods: 2
      Threshold: 70
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
        - Name: AutoScalingGroupName
          Value: !Ref AutoScalingGroup
  
  CPUAlarmLow:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: !Sub '${EnvironmentName}-CPU-Low'
      AlarmDescription: Alarm when CPU falls below 25%
      MetricName: CPUUtilization
      Namespace: AWS/EC2
      Statistic: Average
      Period: 300
      EvaluationPeriods: 2
      Threshold: 25
      ComparisonOperator: LessThanThreshold
      Dimensions:
        - Name: AutoScalingGroupName
          Value: !Ref AutoScalingGroup

Outputs:
  LoadBalancerDNS:
    Description: DNS name of the load balancer
    Value: !GetAtt ApplicationLoadBalancer.DNSName
    Export:
      Name: !Sub '${AWS::StackName}-LoadBalancerDNS'
  
  DatabaseEndpoint:
    Description: RDS database endpoint
    Value: !GetAtt MagentoDatabase.Endpoint.Address
    Export:
      Name: !Sub '${AWS::StackName}-DatabaseEndpoint'
  
  RedisEndpoint:
    Description: ElastiCache Redis endpoint
    Value: !GetAtt MagentoRedisCache.RedisEndpoint.Address
    Export:
      Name: !Sub '${AWS::StackName}-RedisEndpoint'
  
  MediaBucketName:
    Description: S3 bucket for media files
    Value: !Ref MagentoMediaBucket
    Export:
      Name: !Sub '${AWS::StackName}-MediaBucket'
  
  VPCId:
    Description: VPC ID
    Value: !Ref VPC
    Export:
      Name: !Sub '${AWS::StackName}-VPCId'

Save this as magento-cloudformation.yaml.

Step 4: Upload Template to S3

Upload your template to S3:

# Upload template to S3
aws s3 cp magento-cloudformation.yaml s3://magento-cloudformation-templates-<your-bucket-id>/

# Or use the S3 URL in CloudFormation console

Step 5: Deploy Using AWS CLI

Deploy the stack using AWS CLI:

aws cloudformation create-stack \
  --stack-name magento-production \
  --template-body file://magento-cloudformation.yaml \
  --parameters \
    ParameterKey=EnvironmentName,ParameterValue=magento-prod \
    ParameterKey=InstanceType,ParameterValue=t3.large \
    ParameterKey=DBInstanceClass,ParameterValue=db.t3.medium \
    ParameterKey=DBName,ParameterValue=magento2 \
    ParameterKey=DBUsername,ParameterValue=magento_admin \
    ParameterKey=DBPassword,ParameterValue=YourSecurePassword123! \
    ParameterKey=MagentoPublicKey,ParameterValue=your_public_key \
    ParameterKey=MagentoPrivateKey,ParameterValue=your_private_key \
    ParameterKey=AdminEmail,ParameterValue=[email protected] \
    ParameterKey=AdminPassword,ParameterValue=AdminSecurePass123! \
    ParameterKey=KeyPairName,ParameterValue=your-key-pair-name \
  --capabilities CAPABILITY_NAMED_IAM \
  --region us-east-1

Monitor Stack Creation

Watch the stack creation progress:

# Check stack status
aws cloudformation describe-stacks --stack-name magento-production

# Watch stack events
aws cloudformation describe-stack-events --stack-name magento-production --max-items 10

# Or use the console
aws cloudformation describe-stacks --stack-name magento-production --query 'Stacks[0].StackStatus'

Step 6: Alternative - Deploy Using AWS Console

If you prefer the AWS Console:

  1. Navigate to CloudFormation:

    • Go to AWS Console → CloudFormation
    • Click “Create stack”
  2. Choose Template:

    • Select “Upload a template file”
    • Upload your magento-cloudformation.yaml
  3. Specify Stack Details:

    • Stack name: magento-production
    • Fill in all parameters
  4. Configure Stack Options:

    • Add tags if needed
    • Configure stack failure options
  5. Review and Create:

    • Review all settings
    • Acknowledge IAM capabilities
    • Click “Create stack”

Step 7: Post-Deployment Configuration

After the stack is created, perform additional configurations:

Get Stack Outputs

# Get all outputs
aws cloudformation describe-stacks \
  --stack-name magento-production \
  --query 'Stacks[0].Outputs'

# Get specific output
aws cloudformation describe-stacks \
  --stack-name magento-production \
  --query 'Stacks[0].Outputs[?OutputKey==`LoadBalancerDNS`].OutputValue' \
  --output text

Configure S3 for Media Storage

Connect Magento to S3 for media storage:

# Install S3 media extension or configure manually
# Access your EC2 instance
ssh -i your-key.pem ec2-user@<instance-ip>

# Install AWS SDK for PHP
composer require aws/aws-sdk-php

Set Up CloudFront Distribution

Create a CloudFront distribution for CDN:

# Create CloudFront distribution
aws cloudfront create-distribution \
  --origin-domain-name <your-alb-dns-name> \
  --default-root-object index.php

Step 8: Verify Deployment

Check All Services

# Verify EC2 instances
aws ec2 describe-instances \
  --filters "Name=tag:Name,Values=magento-prod-Instance" \
  --query 'Reservations[*].Instances[*].[InstanceId,State.Name,PublicIpAddress]'

# Verify RDS
aws rds describe-db-instances \
  --db-instance-identifier magento-prod-database

# Verify ElastiCache
aws elasticache describe-replication-groups \
  --replication-group-id magento-prod-redis-cache

# Verify Load Balancer
aws elbv2 describe-load-balancers \
  --names magento-prod-ALB

Test Magento Installation

  1. Get the Load Balancer DNS:
ALB_DNS=$(aws cloudformation describe-stacks \
  --stack-name magento-production \
  --query 'Stacks[0].Outputs[?OutputKey==`LoadBalancerDNS`].OutputValue' \
  --output text)

echo "Access your store at: http://$ALB_DNS"
  1. Access the storefront and admin panel
  2. Verify Redis caching is working
  3. Test database connectivity

Step 9: Advanced Configurations

Add EFS for Shared Storage

For multi-instance deployments, add EFS:

EFSFileSystem:
  Type: AWS::EFS::FileSystem
  Properties:
    PerformanceMode: generalPurpose
    Encrypted: true
    FileSystemTags:
      - Key: Name
        Value: !Sub '${EnvironmentName}-EFS'

EFSMountTarget1:
  Type: AWS::EFS::MountTarget
  Properties:
    FileSystemId: !Ref EFSFileSystem
    SubnetId: !Ref PrivateSubnet1
    SecurityGroups:
      - !Ref WebServerSecurityGroup

Add CloudFront CDN

Create a CloudFront distribution:

CloudFrontDistribution:
  Type: AWS::CloudFront::Distribution
  Properties:
    DistributionConfig:
      Origins:
        - DomainName: !GetAtt ApplicationLoadBalancer.DNSName
          Id: ALBOrigin
          CustomOriginConfig:
            HTTPPort: 80
            OriginProtocolPolicy: http-only
      Enabled: true
      DefaultRootObject: index.php
      DefaultCacheBehavior:
        TargetOriginId: ALBOrigin
        ViewerProtocolPolicy: redirect-to-https
        AllowedMethods:
          - DELETE
          - GET
          - HEAD
          - OPTIONS
          - PATCH
          - POST
          - PUT
        CachedMethods:
          - GET
          - HEAD
        ForwardedValues:
          QueryString: true
          Cookies:
            Forward: all

Add SSL Certificate

Use ACM for SSL:

SSLCertificate:
  Type: AWS::CertificateManager::Certificate
  Properties:
    DomainName: yourdomain.com
    ValidationMethod: DNS
    SubjectAlternativeNames:
      - www.yourdomain.com

Step 10: Monitoring and Maintenance

Set Up CloudWatch Alarms

# Create alarm for high CPU
aws cloudwatch put-metric-alarm \
  --alarm-name magento-high-cpu \
  --alarm-description "Alarm when CPU exceeds 80%" \
  --metric-name CPUUtilization \
  --namespace AWS/EC2 \
  --statistic Average \
  --period 300 \
  --threshold 80 \
  --comparison-operator GreaterThanThreshold \
  --evaluation-periods 2

Enable Detailed Monitoring

# Enable detailed monitoring for EC2 instances
aws ec2 monitor-instances --instance-ids <instance-id>

Set Up Automated Backups

# RDS automated backups are already configured
# For file backups, use AWS Backup
aws backup create-backup-plan --backup-plan file://backup-plan.json

Troubleshooting Common Issues

Issue: Stack Creation Fails

Check:

# View stack events
aws cloudformation describe-stack-events \
  --stack-name magento-production \
  --max-items 20

# Check specific resource failures
aws cloudformation describe-stack-resources \
  --stack-name magento-production \
  --logical-resource-id <resource-name>

Issue: EC2 Instances Not Joining Target Group

Solutions:

  1. Check security group rules
  2. Verify user data script executed successfully
  3. Check CloudWatch logs:
aws logs tail /aws/ec2/magento --follow

Issue: Database Connection Failed

Check:

  1. Security group allows traffic from web servers
  2. Database is in private subnet
  3. Endpoint is correct:
aws rds describe-db-instances \
  --db-instance-identifier magento-prod-database \
  --query 'DBInstances[0].Endpoint.Address'

Issue: Redis Connection Failed

Check:

  1. ElastiCache security group configuration
  2. Redis endpoint:
aws elasticache describe-replication-groups \
  --replication-group-id magento-prod-redis-cache \
  --query 'ReplicationGroups[0].NodeGroups[0].PrimaryEndpoint.Address'

Cost Optimization Tips

  1. Use Reserved Instances: For predictable workloads
  2. Right-size Instances: Start small, scale up as needed
  3. Use Spot Instances: For non-critical workloads
  4. Enable S3 Lifecycle Policies: Move old files to Glacier
  5. Use CloudFront: Reduce data transfer costs
  6. Monitor Costs: Set up billing alerts
# Set up billing alert
aws budgets create-budget \
  --account-id <your-account-id> \
  --budget file://budget.json

Security Best Practices

  1. Use Private Subnets: Keep databases and cache in private subnets
  2. Security Groups: Follow least privilege principle
  3. Encryption: Enable encryption at rest and in transit
  4. IAM Roles: Use IAM roles instead of access keys
  5. VPC Flow Logs: Enable for network monitoring
  6. Regular Updates: Keep AMIs and software updated
# Enable VPC Flow Logs
aws ec2 create-flow-logs \
  --resource-type VPC \
  --resource-ids <vpc-id> \
  --traffic-type ALL \
  --log-destination-type s3 \
  --log-destination arn:aws:s3:::your-logs-bucket/

Updating the Stack

To update your stack:

# Update stack
aws cloudformation update-stack \
  --stack-name magento-production \
  --template-body file://magento-cloudformation.yaml \
  --parameters file://parameters.json

Deleting the Stack

To clean up resources:

# Delete stack (this deletes all resources)
aws cloudformation delete-stack --stack-name magento-production

# Monitor deletion
aws cloudformation describe-stacks --stack-name magento-production

Warning: This will delete all resources including databases. Make sure to backup first!

Understanding the Architecture

Request Flow

  1. User Request → CloudFront CDN (if configured)
  2. CloudFront → Application Load Balancer
  3. ALB → EC2 Instance (Auto Scaling selects healthy instance)
  4. EC2 → Checks Redis cache
  5. If cache miss → Queries RDS database
  6. Response → Cached in Redis → Returned to user

Component Responsibilities

  • VPC: Network isolation and security
  • ALB: Traffic distribution and health checks
  • Auto Scaling: Automatic capacity management
  • RDS: Managed database with backups
  • ElastiCache: Fast in-memory caching
  • S3: Scalable media storage
  • CloudFront: Global content delivery

Key Takeaways

  • Infrastructure as Code: CloudFormation makes deployments repeatable
  • High Availability: Multi-AZ deployment ensures uptime
  • Auto Scaling: Automatically handles traffic spikes
  • Managed Services: RDS and ElastiCache reduce operational overhead
  • Security: VPC and security groups provide network isolation
  • Cost Management: Right-size resources and use reserved instances

Next Steps

  1. Set Up CI/CD: Automate deployments with CodePipeline
  2. Configure Monitoring: Set up CloudWatch dashboards
  3. Implement Backups: Automate database and file backups
  4. Add SSL: Configure ACM certificate and HTTPS
  5. Optimize Performance: Tune Redis and database settings
  6. Set Up Logging: Centralize logs with CloudWatch Logs

Resources for Further Learning

Conclusion

Congratulations! You’ve successfully deployed Magento 2 on AWS using CloudFormation. This infrastructure provides:

  • Scalability: Auto Scaling handles traffic automatically
  • Reliability: Multi-AZ deployment ensures high availability
  • Performance: Redis caching and CloudFront CDN optimize speed
  • Security: VPC isolation and security groups protect your infrastructure
  • Maintainability: Infrastructure as Code makes updates easy

Remember:

  • Always test changes in a development environment first
  • Monitor costs and optimize resource usage
  • Keep your CloudFormation templates in version control
  • Document any customizations you make
  • Set up billing alerts to avoid surprises

With this foundation, you can now focus on building your e-commerce business while AWS handles the infrastructure complexity!

Happy deploying! 🚀

Comments