<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by MAYOWA (CloudSensei) on Medium]]></title>
        <description><![CDATA[Stories by MAYOWA (CloudSensei) on Medium]]></description>
        <link>https://medium.com/@cloudsenseing?source=rss-7ce0de500635------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*DifuoBSjhOsYJTkw</url>
            <title>Stories by MAYOWA (CloudSensei) on Medium</title>
            <link>https://medium.com/@cloudsenseing?source=rss-7ce0de500635------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Thu, 14 May 2026 21:22:08 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@cloudsenseing/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Cloud Project 1: Deploying a Highly Available WordPress Architecture on AWS with Terraform]]></title>
            <link>https://towardsaws.com/cloud-project-1-deploying-a-highly-available-wordpress-architecture-on-aws-with-terraform-408346c39dfe?source=rss-7ce0de500635------2</link>
            <guid isPermaLink="false">https://medium.com/p/408346c39dfe</guid>
            <category><![CDATA[cloud-project]]></category>
            <category><![CDATA[terraform]]></category>
            <category><![CDATA[cloud-computing]]></category>
            <category><![CDATA[aws]]></category>
            <dc:creator><![CDATA[MAYOWA (CloudSensei)]]></dc:creator>
            <pubDate>Sat, 02 Mar 2024 12:29:52 GMT</pubDate>
            <atom:updated>2024-03-10T02:50:53.438Z</atom:updated>
            <content:encoded><![CDATA[<p>In this project, I embarked on creating a robust and scalable WordPress architecture hosted on Amazon Web Services (AWS) while leveraging Terraform for infrastructure provisioning and management. The goal was to ensure high availability, fault tolerance, and seamless scalability to accommodate varying levels of traffic.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2CVwByiCybEZ6tIn5Et3YA.png" /><figcaption>Architecture Diagram</figcaption></figure><p><strong>Terminologies &amp; Use Cases:</strong></p><ul><li><strong>Amazon CloudFront: </strong>CloudFront optimizes WordPress performance by caching and delivering static content from edge locations, reducing latency and offloading traffic from origin servers. It enhances scalability, security, and user experience by serving content closer to end-users and supporting HTTPS encryption for data privacy. This is a free-tier project, so route53 was not configured.</li><li><strong>Amazon S3: </strong>Amazon S3 serves as a reliable storage solution for media content and Terraform state files. S3 efficiently stores and retrieves media assets such as images and videos, ensuring seamless delivery to users while reducing the load on the WordPress application servers. Additionally, S3 securely stores Terraform state files, enabling collaborative infrastructure management and ensuring consistency across deployments.</li><li><strong>Amazon DynamoDB: </strong>DynamoDB is used for Terraform state locking. It ensures exclusive access during infrastructure updates, enhancing reliability and preventing conflicts.</li><li><strong>NAT Gateway: </strong>A<strong> </strong>NAT Gateway is employed to facilitate outbound internet traffic from private subnets in the AWS environment. It acts as a central gateway for instances in private subnets to access resources outside the AWS network while maintaining security through Network Address Translation (NAT).</li><li><strong>Availability Zones: </strong>Availability Zones (AZs) are utilized for deploying resources across multiple isolated locations within an AWS region. By distributing resources across different AZs, the project ensures high availability and fault tolerance. In case of failure in one AZ, resources in other zones continue to function, minimizing downtime and ensuring uninterrupted service.</li><li><strong>VPC: </strong>A Virtual Private Cloud (VPC) is employed as the networking foundation within the AWS environment. The VPC enables the creation of isolated virtual networks, allowing fine-grained control over network configurations, including IP address ranges, subnets, route tables, and network gateways.</li><li><strong>Subnets: </strong>Public subnets facilitate internet-facing services, while private subnets ensure the confidentiality and integrity of internal resources.</li><li><strong>AutoScaling Groups: </strong>Autoscaling Groups are a critical component of this project, enabling automatic scaling of EC2 instances based on predefined conditions such as CPU utilization or traffic load. By configuring Autoscaling Groups, infrastructure can dynamically adjust to meet demand, scaling out during periods of high traffic and scaling in during quieter times.</li><li><strong>Application LoadBalancer: </strong>ALB operates at the application layer (Layer 7) of the OSI model, allowing it to route traffic based on content within the HTTP request, such as URL paths or hostnames. ALB seamlessly integrates with other AWS services like Autoscaling Groups and AWS Certificate Manager, enhancing the project’s overall infrastructure and security posture. Its built-in health checks monitor the health of backend instances, automatically routing traffic away from unhealthy instances to maintain optimal application availability.</li><li><strong>Amazon Elasticache (MemCached): </strong>Amazon ElastiCache for Memcached optimizes application performance by caching frequently accessed data in memory. It reduces latency, improves scalability by offloading database read operations, and ensures high availability with automatic cluster scaling.</li><li><strong>Amazon Aurora (MySQL): </strong>Amazon Aurora for MySQL serves as the relational database solution, offering high performance, scalability, and reliability for storing application data. Aurora’s distributed architecture enhances resilience and fault tolerance, ensuring continuous availability and data durability.</li><li><strong>Amazon Elastic FileSystem (EFS): </strong>Amazon Elastic File System (EFS) is utilized as a scalable and highly available file storage solution for the application. EFS provides a shared file system accessible by multiple EC2 instances, enabling data sharing and collaboration across the application infrastructure. By leveraging EFS, the project ensures data consistency and durability, with automatic scalability to accommodate growing storage needs.</li><li><strong>Amazon CloudWatch: </strong>Amazon CloudWatch is utilized to monitor the CPU utilization of the Autoscaling Groups. CloudWatch collects and tracks metrics related to CPU usage across the EC2 instances within the Autoscaling Group. By setting up CloudWatch alarms based on predefined thresholds for CPU utilization, the project ensures proactive monitoring of resource usage using CloudWatch alarms based on predefined thresholds for CPU utilization ensuring proactive monitoring of resource usage.</li><li><strong>Amazon SNS: </strong>In this project, Amazon Simple Notification Service (SNS) is used to notify subscribers based on the CloudWatch alarms triggered by CPU utilization metrics. When CloudWatch detects CPU utilization exceeding or falling below predefined thresholds for Autoscaling Groups, it sends notifications to the SNS topic which is then delivered to subscribers of the topic.</li><li><strong>AWS Systems Manager: </strong>In this project, AWS Systems Manager (SSM) manages instance profiles for EC2 instances, configures Amazon EFS mount points, and securely distributes credentials for accessing the Amazon S3 bucket containing media content.</li></ul><p><strong>Pre-requisites:</strong></p><ul><li>Code Editor: Visual Studio Code (preferred)</li><li><a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html">AWS CLI v2</a> configured with your IAM credentials for programmatic access</li><li><a href="https://developer.hashicorp.com/terraform/downloads">Terraform</a> CLI</li></ul><p><strong>File Structure:</strong></p><pre>.<br>├── modules<br>│   ├── aurora<br>│   │   ├── main.tf<br>│   │   ├── output.tf<br>│   │   ├── securitygroup.tf<br>│   │   └── variables.tf<br>│   ├── cloudfront<br>│   │   ├── main.tf<br>│   │   ├── output.tf<br>│   │   └── variables.tf<br>│   ├── efs<br>│   │   ├── main.tf<br>│   │   ├── output.tf<br>│   │   ├── securitygroup.tf<br>│   │   └── variables.tf<br>│   ├── elasticache<br>│   │   ├── main.tf<br>│   │   ├── output.tf<br>│   │   ├── securitygroup.tf<br>│   │   └── variables.tf<br>│   ├── loadbalancer<br>│   │   ├── main.tf<br>│   │   ├── output.tf<br>│   │   └── variables.tf<br>│   ├── securitygroup<br>│   │   ├── main.tf<br>│   │   ├── output.tf<br>│   │   └── variables.tf<br>│   ├── vpc<br>│   │   ├── main.tf<br>│   │   ├── output.tf<br>│   │   └── variables.tf<br>│   └── wordpress<br>│       ├── main.tf<br>│       ├── output.tf<br>│       └── variables.tf<br>├── scripts<br>│   └── init-aml2.sh<br>├── backend.tf<br>├── backend_resource.tf<br>├── buckets.tf<br>├── cloudwatch.tf<br>├── data.tf<br>├── iam.tf<br>├── main.tf<br>├── output.tf<br>├── providers.tf<br>├── sns.tf<br>├── terraform.tfvars<br>└── variables.tf</pre><p><strong>Terraform Deployment Procedures: </strong>The standard to deploy terraform configuration files include:</p><ul><li>Writing Configuration: Create Terraform configuration files (.tf) defining the desired infrastructure resources, including providers, resources, and configurations.</li><li>Initializing Terraform: Run terraform init in the directory containing the configuration files. This command initializes the working directory, downloads provider plugins, and prepares the environment for Terraform operations.</li><li>Planning Deployment: Run terraform plan to generate an execution plan. Terraform compares the desired state defined in the configuration files with the current state of the infrastructure and identifies the changes required to achieve the desired state.</li><li>Applying Changes: Run terraform apply to apply the execution plan and provision or modify the infrastructure resources accordingly. Terraform interacts with the cloud provider&#39;s API to create, update, or delete resources as necessary.</li><li>Review and Confirm: Terraform prompts for confirmation before making any changes to the infrastructure. Review the execution plan and confirm the changes to proceed with deployment.</li><li>Monitor and Manage: After deployment, Terraform tracks the state of the infrastructure. Use commands like terraform show and terraform state to inspect the current state, and terraform destroy to remove the provisioned resources if needed.</li></ul><p><strong>Understanding the Code:</strong></p><p>In the providers.tf configuration, the version, and the source of the required provider are defined within the Terraform block, specifying that the “hashicorp/aws” provider, version “~&gt; 5.0,” is necessary. The provider block configures the AWS provider with the specified region dynamically set to the value of the “var.region” variable.</p><pre>terraform {<br>  required_providers {<br>    aws = {<br>      source  = &quot;hashicorp/aws&quot;<br>      version = &quot;~&gt; 5.0&quot;<br>    }<br>  }<br>}<br><br>provider &quot;aws&quot; {<br>  region = var.region<br>}</pre><p>The backend.tf configuration file is specified to use Amazon S3 as the storage backend. The configuration includes settings for the S3 bucket, the key within the bucket where state files are stored, the AWS region where the bucket resides, the DynamoDB table used for state locking, and enabling encryption for data at rest within the S3 bucket.</p><pre>terraform {<br>  backend &quot;s3&quot; {<br>    bucket         = &quot;terraform-state-demo-wp&quot;<br>    key            = &quot;wordpress-state/terraform.tfstate&quot;<br>    region         = &quot;us-east-1&quot;<br>    dynamodb_table = &quot;state-lock-db&quot;<br>    encrypt        = true<br>  }<br>}</pre><p>The backend_resource.tf configuration includes the creation of two essential resources. Firstly, it defines a DynamoDB table named “terraform_lock” to serve as a state lock mechanism, ensuring safe concurrent access to Terraform state files. This table is configured with attributes such as billing mode and hash key, and it’s set to prevent accidental deletion. Secondly, it establishes an S3 bucket named “terraform_state” for storing Terraform state files securely. This bucket’s configuration includes enabling server-side encryption and versioning to enhance data protection and facilitate version management.</p><pre>resource &quot;aws_dynamodb_table&quot; &quot;terraform_lock&quot; {<br>  name         = var.dynamodb_table<br>  billing_mode = var.billing_mode<br>  hash_key     = var.hash_key<br>  attribute {<br>    name = var.attribute_name<br>    type = var.attribute_type<br>  }<br>  lifecycle {<br>    prevent_destroy = true<br>  }<br>}<br><br>resource &quot;aws_s3_bucket&quot; &quot;terraform_state&quot; {<br>  bucket        = var.backend_bucket<br>  force_destroy = true<br>  lifecycle {<br>    prevent_destroy = true<br>  }<br>}<br><br>resource &quot;aws_s3_bucket_server_side_encryption_configuration&quot; &quot;bucket_encryption&quot; {<br>  bucket = aws_s3_bucket.terraform_state.id<br>  rule {<br>    apply_server_side_encryption_by_default {<br>      sse_algorithm = &quot;AES256&quot;<br>    }<br>  }<br>}<br><br>resource &quot;aws_s3_bucket_versioning&quot; &quot;bucket_version&quot; {<br>  bucket = aws_s3_bucket.terraform_state.id<br>  versioning_configuration {<br>    status = &quot;Enabled&quot;<br>  }<br>}</pre><p>The buckets.tf configuration creates an S3 bucket named “bucket” with the specified name from the “var.object_bucket” variable. The bucket’s configuration includes enabling force destroy to allow for deletion even if non-empty. It sets up bucket ACL (Access Control List) to “private” for restricted access, and it ensures bucket ownership controls are configured to “BucketOwnerPreferred.” Furthermore, the configuration encrypts the bucket’s contents using SSE-S3 (Server-Side Encryption) with AES256 encryption algorithm for enhanced security.</p><pre>resource &quot;aws_s3_bucket&quot; &quot;bucket&quot; {<br>  bucket        = var.object_bucket<br>  force_destroy = true<br>}<br><br>resource &quot;aws_s3_bucket_acl&quot; &quot;bucket_acl&quot; {<br>  bucket = aws_s3_bucket.bucket.id<br>  acl    = &quot;private&quot;<br><br>  depends_on = [aws_s3_bucket_ownership_controls.ownership]<br>}<br><br>resource &quot;aws_s3_bucket_ownership_controls&quot; &quot;ownership&quot; {<br>  bucket = aws_s3_bucket.bucket.id<br>  rule {<br>    object_ownership = &quot;BucketOwnerPreferred&quot;<br>  }<br>}<br><br>resource &quot;aws_s3_bucket_server_side_encryption_configuration&quot; &quot;encrypt&quot; {<br>  bucket = aws_s3_bucket.bucket.id<br>  rule {<br>    apply_server_side_encryption_by_default {<br>      sse_algorithm = &quot;AES256&quot;<br>    }<br>  }<br>}</pre><p>The cloudwatch.tf configuration file defines a CloudWatch metric alarm named “ec2_cpu_alarm” to monitor CPU utilization of EC2 instances. The alarm is set to trigger when the CPU utilization exceeds or equals the threshold of 80% over a period of 2 evaluation periods, each lasting for 60 seconds. It uses the “Average” statistic to evaluate the metric. The alarm description provides information about its purpose, and it specifies that missing data should not be considered breaching. When triggered, the alarm sends notifications to an SNS topic defined in “aws_sns_topic.topic.arn”. The alarm is also associated with the Auto Scaling Group named “module.app.app_asg_name” to monitor CPU utilization across EC2 instances within the group.</p><pre>resource &quot;aws_cloudwatch_metric_alarm&quot; &quot;ec2_cpu_alarm&quot; {<br><br>  comparison_operator = &quot;GreaterThanOrEqualToThreshold&quot;<br>  evaluation_periods  = &quot;2&quot;<br>  metric_name         = &quot;CPUUtilization&quot;<br>  namespace           = &quot;AWS/EC2&quot;<br>  period              = &quot;60&quot;<br>  statistic           = &quot;Average&quot;<br>  threshold           = &quot;80&quot;<br>  alarm_description   = &quot;This metric monitors ec2 cpu utilization&quot;<br>  treat_missing_data  = &quot;notBreaching&quot;<br>  alarm_actions       = [aws_sns_topic.topic.arn]<br>  <br>  alarm_name = &quot;cpu-utilization&quot;<br>  dimensions = {<br>    AutoScalingGroupName = module.app.app_asg_name<br>  } <br>}</pre><p>The iam.tf configuration file sets up several AWS IAM (Identity and Access Management) resources to manage permissions for a WordPress application. Firstly, it defines an IAM role named “WordPressExecutionRole” with permissions to assume roles for EC2 instances. Then, it attaches policies to this role to allow actions related to Systems Manager (SSM), Amazon Elastic File System (EFS), Amazon S3, and Amazon CloudWatch Logs. These policies grant necessary permissions for the WordPress application to interact with AWS services securely. It also creates an IAM instance profile named “wordpress-instance-profile” associated with the WordPressExecutionRole, enabling EC2 instances to assume this role and access AWS resources based on defined policies.</p><pre>resource &quot;aws_iam_instance_profile&quot; &quot;instance_profile&quot; {<br>  name = &quot;wordpress-instance-profile&quot;<br>  role = aws_iam_role.wordpress_execution_role.name<br>}<br><br>resource &quot;aws_iam_role&quot; &quot;wordpress_execution_role&quot; {<br>  name = &quot;WordPressExecutionRole&quot;<br><br>  assume_role_policy = jsonencode({<br>    Version = &quot;2012-10-17&quot;,<br>    Statement = [<br>      {<br>        Sid    = &quot;&quot;,<br>        Effect = &quot;Allow&quot;,<br>        Principal = {<br>          Service = &quot;ec2.amazonaws.com&quot;<br>        },<br>        Action = &quot;sts:AssumeRole&quot;<br>      }<br>    ]<br>  })<br>}<br><br>resource &quot;aws_iam_role_policy&quot; &quot;efs_execution_role_policy&quot; {<br>  name = &quot;EFSFileSystemUtils&quot;<br>  role = aws_iam_role.wordpress_execution_role.id<br>  policy = jsonencode({<br>    Version = &quot;2012-10-17&quot;,<br>    Statement = [<br>      {<br>        Effect = &quot;Allow&quot;,<br>        Action = [<br>          &quot;ssm:DescribeAssociation&quot;,<br>          &quot;ssm:GetDeployablePatchSnapshotForInstance&quot;,<br>          &quot;ssm:GetDocument&quot;,<br>          &quot;ssm:DescribeDocument&quot;,<br>          &quot;ssm:GetManifest&quot;,<br>          &quot;ssm:GetParameter&quot;,<br>          &quot;ssm:GetParameters&quot;,<br>          &quot;ssm:ListAssociations&quot;,<br>          &quot;ssm:ListInstanceAssociations&quot;,<br>          &quot;ssm:PutInventory&quot;,<br>          &quot;ssm:PutComplianceItems&quot;,<br>          &quot;ssm:PutConfigurePackageResult&quot;,<br>          &quot;ssm:UpdateAssociationStatus&quot;,<br>          &quot;ssm:UpdateInstanceAssociationStatus&quot;,<br>          &quot;ssm:UpdateInstanceInformation&quot;<br>        ],<br>        Resource = &quot;*&quot;<br>      },<br>      {<br>        Effect = &quot;Allow&quot;,<br>        Action = [<br>          &quot;ssmmessages:CreateControlChannel&quot;,<br>          &quot;ssmmessages:CreateDataChannel&quot;,<br>          &quot;ssmmessages:OpenControlChannel&quot;,<br>          &quot;ssmmessages:OpenDataChannel&quot;<br>        ],<br>        Resource = &quot;*&quot;<br>      },<br>      {<br>        Effect = &quot;Allow&quot;,<br>        Action = [<br>          &quot;ec2messages:AcknowledgeMessage&quot;,<br>          &quot;ec2messages:DeleteMessage&quot;,<br>          &quot;ec2messages:FailMessage&quot;,<br>          &quot;ec2messages:GetEndpoint&quot;,<br>          &quot;ec2messages:GetMessages&quot;,<br>          &quot;ec2messages:SendReply&quot;<br>        ],<br>        Resource = &quot;*&quot;<br>      },<br>      {<br>        Effect = &quot;Allow&quot;,<br>        Action = [<br>          &quot;elasticfilesystem:DescribeMountTargets&quot;<br>        ],<br>        Resource = &quot;*&quot;<br>      },<br>      {<br>        Effect = &quot;Allow&quot;,<br>        Action = [<br>          &quot;ec2:DescribeAvailabilityZones&quot;<br>        ],<br>        Resource = &quot;*&quot;<br>      },<br>      {<br>        Effect = &quot;Allow&quot;,<br>        Action = [<br>          &quot;logs:PutLogEvents&quot;,<br>          &quot;logs:DescribeLogStreams&quot;,<br>          &quot;logs:DescribeLogGroups&quot;,<br>          &quot;logs:CreateLogStream&quot;,<br>          &quot;logs:CreateLogGroup&quot;,<br>          &quot;logs:PutRetentionPolicy&quot;<br>        ],<br>        Resource = &quot;*&quot;<br>      }<br>    ]<br>  })<br>}<br><br>resource &quot;aws_iam_role_policy&quot; &quot;ssm_execution_role_policy&quot; {<br>  name = &quot;SSMManagedInstanceCore&quot;<br>  role = aws_iam_role.wordpress_execution_role.id<br>  policy = jsonencode({<br><br>    Version = &quot;2012-10-17&quot;,<br>    Statement = [<br>      {<br>        Effect = &quot;Allow&quot;,<br>        Action = [<br>          &quot;ssm:DescribeAssociation&quot;,<br>          &quot;ssm:GetDeployablePatchSnapshotForInstance&quot;,<br>          &quot;ssm:GetDocument&quot;,<br>          &quot;ssm:DescribeDocument&quot;,<br>          &quot;ssm:GetManifest&quot;,<br>          &quot;ssm:GetParameter&quot;,<br>          &quot;ssm:GetParameters&quot;,<br>          &quot;ssm:ListAssociations&quot;,<br>          &quot;ssm:ListInstanceAssociations&quot;,<br>          &quot;ssm:PutInventory&quot;,<br>          &quot;ssm:PutComplianceItems&quot;,<br>          &quot;ssm:PutConfigurePackageResult&quot;,<br>          &quot;ssm:UpdateAssociationStatus&quot;,<br>          &quot;ssm:UpdateInstanceAssociationStatus&quot;,<br>          &quot;ssm:UpdateInstanceInformation&quot;<br>        ],<br>        Resource = &quot;*&quot;<br>      },<br>      {<br>        Effect = &quot;Allow&quot;,<br>        Action = [<br>          &quot;ssmmessages:CreateControlChannel&quot;,<br>          &quot;ssmmessages:CreateDataChannel&quot;,<br>          &quot;ssmmessages:OpenControlChannel&quot;,<br>          &quot;ssmmessages:OpenDataChannel&quot;<br>        ],<br>        Resource = &quot;*&quot;<br>      },<br>      {<br>        Effect = &quot;Allow&quot;,<br>        Action = [<br>          &quot;ec2messages:AcknowledgeMessage&quot;,<br>          &quot;ec2messages:DeleteMessage&quot;,<br>          &quot;ec2messages:FailMessage&quot;,<br>          &quot;ec2messages:GetEndpoint&quot;,<br>          &quot;ec2messages:GetMessages&quot;,<br>          &quot;ec2messages:SendReply&quot;<br>        ],<br>        Resource = &quot;*&quot;<br>      }<br>    ]<br>  })<br>}<br><br>resource &quot;aws_iam_role_policy&quot; &quot;s3_bucket_policy&quot; {<br>  name = &quot;S3FullAccess&quot;<br>  role = aws_iam_role.wordpress_execution_role.id<br>  policy = jsonencode({<br><br>    Version = &quot;2012-10-17&quot;,<br>    Statement = [{<br>      Effect = &quot;Allow&quot;,<br>      Action = [<br>        &quot;s3:*&quot;,<br>        &quot;s3-object-lambda:*&quot;<br>      ],<br>      Resource = &quot;*&quot;<br>    }]<br>  })<br>}<br><br></pre><p>The sns.tf configuration file creates an AWS SNS (Simple Notification Service) topic named “CPUUtilizationAlarm” with the specified name and tags. Additionally, it defines an SNS topic subscription for email notifications, with the count determined by the length of the provided email addresses. The subscription associates each email address with the SNS topic, enabling email notifications to be sent to the specified recipients when events are published to the topic.</p><pre>resource &quot;aws_sns_topic&quot; &quot;topic&quot; {<br>  name = &quot;CPUUtilizationAlarm&quot;<br>  tags = {<br>    Name = &quot;TestAlarm&quot;<br>  }<br>}<br><br>resource &quot;aws_sns_topic_subscription&quot; &quot;email_subscription&quot; {<br>  count     = length(var.email_address)<br>  topic_arn = aws_sns_topic.topic.arn<br>  protocol  = &quot;email&quot;<br>  endpoint  = var.email_address[count.index]<br>}</pre><p>The main.tf configuration file defines the infrastructure deployment for the WordPress application on AWS, organized into modular components. Here’s the breakdown of the code below:</p><pre>locals {<br>  availability_zone_a = data.aws_availability_zones.availability_zones.names[0]<br>  availability_zone_b = data.aws_availability_zones.availability_zones.names[1]<br>}<br><br>module &quot;app&quot; {<br>  source = &quot;./modules/wordpress&quot;<br><br>  instance_type          = var.app_instance_type<br>  image_id               = data.aws_ami.amazon_linux_2_latest.id<br>  vpc_security_group_ids = [&quot;${module.app.app_security_group_id}&quot;]<br>  user_data = base64encode(&quot;${templatefile(&quot;${path.module}/scripts/init-aml2.sh&quot;, {<br>    DB_HOST     = &quot;${module.database.rds_endpoint}&quot;<br>    DB_NAME     = &quot;${module.database.rds_database_name}&quot;<br>    DB_PASSWORD = &quot;${module.database.rds_password}&quot;<br>    DB_USER     = &quot;${module.database.rds_username}&quot;<br>    EFS_ID      = &quot;${module.filesystem.efs_id}&quot;<br>  })}&quot;)<br><br>  target_group_arns     = [module.loadbalancer.target_group_arn]<br>  vpc_zone_identifier   = [module.vpc.private_subnet_a, module.vpc.private_subnet_b]<br>  security_group_vpc_id = module.vpc.vpc_id<br>  iam_arn               = aws_iam_instance_profile.instance_profile.arn<br><br>  depends_on = [module.database.rds_cluster_instances, module.filesystem.efs_filesystem, module.cache.cache_cluster]<br>}<br><br>module &quot;database&quot; {<br>  source = &quot;./modules/aurora&quot;<br><br>  master_username          = var.db_username<br>  master_password          = var.db_password<br>  vpc_security_group_ids   = [module.database.db_security_group_id]<br>  source_security_group_id = module.app.app_security_group_id<br>  subnet_ids               = [module.vpc.private_subnet_c, module.vpc.private_subnet_d]<br>  availability_zones       = [local.availability_zone_a, local.availability_zone_b]<br>  vpc_id                   = module.vpc.vpc_id<br>  security_group_id        = module.database.db_security_group_id<br>}<br><br>module &quot;cache&quot; {<br>  source = &quot;./modules/elasticache&quot;<br><br>  security_group_id        = module.cache.cache_security_group_id<br>  source_security_group_id = module.app.app_security_group_id<br>  security_group_ids       = [module.cache.cache_security_group_id]<br>  cache_subnets            = [module.vpc.private_subnet_c, module.vpc.private_subnet_d]<br>  vpc_id                   = module.vpc.vpc_id<br>  availability_zones       = [local.availability_zone_a, local.availability_zone_b]<br>}<br><br>module &quot;filesystem&quot; {<br>  source = &quot;./modules/efs&quot;<br><br>  subnet_a                 = module.vpc.private_subnet_c<br>  subnet_b                 = module.vpc.private_subnet_d<br>  vpc_id                   = module.vpc.vpc_id<br>  security_group_id        = module.filesystem.fs_security_group_id<br>  source_security_group_id = module.app.app_security_group_id<br>  security_groups          = [module.filesystem.fs_security_group_id]<br>}<br><br>module &quot;loadbalancer&quot; {<br>  source = &quot;./modules/loadbalancer&quot;<br><br>  subnets               = [module.vpc.public_subnet_a, module.vpc.public_subnet_b]<br>  security_groups       = [&quot;${module.loadbalancer.loadbalancer_security_group_id}&quot;]<br>  vpc_id                = module.vpc.vpc_id<br>  security_group_vpc_id = module.vpc.vpc_id<br>}<br><br>module &quot;cloudfront&quot; {<br>  source = &quot;./modules/cloudfront&quot;<br><br>  domain_name = module.loadbalancer.loadbalancer_dns<br>  origin_id   = module.loadbalancer.loadbalancer_dns<br>}<br><br><br>module &quot;vpc&quot; {<br>  source = &quot;./modules/vpc&quot;<br><br>  az_a = local.availability_zone_a<br>  az_b = local.availability_zone_b<br>}</pre><p>1. <strong>Locals Block</strong>: Defines local variables availability_zone_a and availability_zone_b representing the first two availability zones in the AWS region.</p><p>2. “<strong>app” Module</strong>: Deploys the WordPress application using a custom module located in ./modules/wordpress<br> — Configures instance type, AMI image ID, security groups, user data for EC2 instances, target group ARNs, VPC subnets, and IAM role.</p><p>3. “<strong>database” Module</strong>: Deploys an Aurora database using a custom module located in ./modules/auora<br> — Configures master username, password, security groups, subnets, availability zones, and VPC.</p><p>4. <strong>“cache” Module: </strong>Deploys an ElastiCache cluster using a custom module located in ./modules/elasticache<br> — Configures security groups, subnets, availability zones, and VPC.</p><p>5. <strong>“filesystem” Module</strong>: Deploys an Amazon EFS filesystem using a custom module located in ./modules/efs<br> — Configures subnets, security groups, and VPC.</p><p>6. <strong>“loadbalancer” Module</strong>: Deploys an Application Load Balancer using a custom module located in ./modules/loadbalancer<br> — Configures subnets, security groups, and VPC.</p><p>7. <strong>“cloudfront” Module</strong>: Deploys an Amazon CloudFront distribution using a custom module located in ./modules/cloudfront<br> — Configures the domain name and origin ID.</p><p>8. <strong>“vpc” Module</strong>: Deploys the VPC infrastructure using a custom module located in ./modules/vpc<br> — Configures availability zones.</p><p>The data.tf configuration defines two data sources that are defined to retrieve information from AWS: “aws_availability_zones” and “aws_ami.” The “aws_availability_zones” data source fetches information about available availability zones, specifying the state as “available.” The “aws_ami” data source retrieves the latest Amazon Linux 2 AMI (Amazon Machine Image) owned by Amazon, filtering by name and architecture to match the specified criteria.</p><pre>data &quot;aws_availability_zones&quot; &quot;availability_zones&quot; {<br>  state = &quot;available&quot;<br>}<br><br>data &quot;aws_ami&quot; &quot;amazon_linux_2_latest&quot; {<br>  owners = [&quot;amazon&quot;]<br><br>  most_recent = true<br><br>  filter {<br>    name   = &quot;name&quot;<br>    values = [&quot;amzn2-ami-hvm-*-x86_64-gp2&quot;]<br>  }<br><br>  filter {<br>    name   = &quot;architecture&quot;<br>    values = [&quot;x86_64&quot;]<br>  }<br>}</pre><p>In the output.tf configuration file we’ve set up two friendly outputs to make managing your deployment a breeze. The first output, the Load Balancer URL, serves as your website’s welcoming entrance, providing the DNS name where visitors can access your site. It’s like the front door to your online space! The second output, the CloudFront URL, acts as a magic carpet ride for your site, ensuring it’s lightning-fast and accessible worldwide.</p><pre>output &quot;loadbalancer&quot; {<br>  value = module.loadbalancer.loadbalancer_dns<br>}<br><br>output &quot;cloudfront_url&quot; {<br>  value = module.cloudfront.cloudfront_url<br>}</pre><p>The init-aml2.sh script helps in getting WordPress up and running smoothly on your Amazon Linux 2 EC2 instance. It takes care of everything from installing necessary packages to configuring Memcached for faster performance. Plus, it seamlessly integrates with Amazon EFS for shared storage and ensures your WordPress site is ready to shine with Apache and PHP. With just a few steps, your WordPress environment will be optimized and ready for action!</p><pre>#!/bin/bash<br><br># Assign environment variables to local variables<br>DB_HOST=&quot;${DB_HOST}&quot;<br>DB_NAME=&quot;${DB_NAME}&quot;<br>DB_PASSWORD=&quot;${DB_PASSWORD}&quot;<br>DB_USER=&quot;${DB_USER}&quot;<br>EFS_ID=&quot;${EFS_ID}&quot;<br>FILE=&quot;/var/www/html/latest.tar.gz&quot;<br><br># Update the system and install necessary packages<br>sudo yum update -y<br>sudo yum install -y amazon-efs-utils<br>sudo yum install -y nfs-utils<br>sudo mkdir -p /var/www/html/<br>sudo mount -t efs -o tls $EFS_ID:/ /var/www/html<br><br># Install and start Memcached<br>sudo yum install memcached -y<br>sudo systemctl start memcached<br>sudo systemctl enable memcached<br><br># Install Apache, PHP, and related packages<br>sudo yum install -y httpd<br>sudo yum remove -y php*<br>sudo yum clean -y all<br>sudo amazon-linux-extras enable php8.0 memcached1.5<br>sudo yum clean -y metadata<br>sudo yum install -q -y php php-gd php-mysqli php-cli php-fpm php-opcache php-common<br>sudo yum install -y php-xml<br>sudo yum install -y gcc make php php-pear php-devel libmemcached libmemcached-devel zlib-devel memcached<br>sudo pecl update-channels<br>&quot;echo n n n n n n y y&quot; | sudo pecl install memcached<br><br># Start Apache web server<br>sudo systemctl start httpd<br>sudo systemctl enable httpd<br><br># Download and configure WordPress<br>if [[ -e $FILE ]]; then<br>    sleep 300<br>else  <br>    cd /var/www/html<br>    sudo wget https://wordpress.org/latest.tar.gz<br>    sudo tar -xzvf latest.tar.gz<br>    sudo cp -r wordpress/* .<br>    cd ..<br>    sudo chown -R apache:apache html<br>    cd ./html<br>    sudo rm -rf wordpress/<br>    sudo rm -rf latest.tar.gz<br>    sudo cp wp-config-sample.php wp-config.php<br>    sudo chown -R apache:apache wp-config.php<br>    cat &lt;&lt;EOT &gt;&gt; credfile.txt<br>define( &#39;AS3CF_SETTINGS&#39;, serialize( array (<br>    &#39;provider&#39; =&gt; &#39;aws&#39;,<br>    &#39;use-server-roles&#39; =&gt; true,<br>) ) );<br>EOT<br>    sudo sed -i &quot;s/database_name_here/$DB_NAME/&quot; wp-config.php<br>    sudo sed -i &quot;s/username_here/$DB_USER/&quot; wp-config.php<br>    sudo sed -i &quot;s/password_here/$DB_PASSWORD/&quot; wp-config.php<br>    sudo sed -i &quot;s/localhost/$DB_HOST/&quot; wp-config.php<br>    sudo sed -i &quot;/define( &#39;WP_DEBUG&#39;, false );/r credfile.txt&quot; wp-config.php<br>    sudo rm credfile.txt<br>fi<br><br># Add Memcached extension to PHP configuration<br>echo extension=memcached.so | sudo tee -a /etc/php.ini<br><br># Restart Apache to apply changes<br>sudo systemctl restart httpd</pre><p><strong>Results:</strong></p><p>In this section, we will visually explore the outcome of the deployment of our infrastructure. Also, we will set up our WordPress deployment for caching with the “W3TC” plugin to improve database performance and offload static content from our EC2 instance storage(EBS) utilizing the high availability and redundancy of S3 using the “WP Offload Media Lite” plugin.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/619/1*ifm6VLDyvwTLsq3v9ixz6A.png" /><figcaption>S3 Remote State and DynamoDB Lock</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*JXkiKryy2hNVydctaO9e9g.gif" /><figcaption>Running “terraform apply”</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*wG4B6hB-sY2m_OsJuI-L-g.png" /><figcaption>Deployed VPC Resource Map</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/943/1*aTVUnNHrUcrdv2TXDGSXbw.png" /><figcaption>SNS Topic Subscription Confirmation Mail</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/648/1*QHp6yWX79k57MKxeeNR-dg.png" /><figcaption>Terraform Apply Complete with Output</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*cZ1nMk04Z23f8hq8PhJgzw.gif" /><figcaption>Testing Cloudfront URL</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*FugEjvJSD5X7xzMeFVnbeA.png" /><figcaption>Autoscaling Group EC2 Instance Deployments</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/506/1*CZjDc8CUk6gkILJg9ZXTCQ.png" /><figcaption>Systems Manager for Remote Access to Instances</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9S37ya9YS8HezJ3km0GPQg.png" /><figcaption>WordPress Dashboard</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*9pRbhLL5tKXiuapM9udPeg.png" /><figcaption>State Bucket Resource and WordPress Content Bucket</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CPKuC2WkHNhMcTKLO_XadA.png" /><figcaption>EFS Deployment with Mount Targets</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nlkpvh0lmL89ts_9G91dsw.png" /><figcaption>Amazon Aurora DB Cluster Deployment</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DC_nkYdUK5U3QnuskCIDwg.png" /><figcaption>Memcached Cluster Deployment with Nodes</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UA-G3nM92NSC8xtgvKAz6A.png" /><figcaption>Application LoadBalancer Deployment</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oM9eQWTvidwdpWqdoPCa6g.png" /><figcaption>Target Group Deployment</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZagrdWMcmTpbEPetvmSvvw.png" /><figcaption>Cloudfront Distribution Deployment</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*PYY_ograTfYlXOmb1o5mpw.png" /><figcaption>CloudWatch CPUUtilizaton Alarm</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*vyPUJgFRiIu-yFQV6VGm_g.png" /><figcaption>CPUUtilization Alarm SNS Topic</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1fUV2WNaAEmh5ppMSCWrSQ.png" /><figcaption>Installed the W3TC Cache Plugin to Setup MemCached</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*cyYlRwRXVpYQz6LMRO74Gw.png" /><figcaption>Testing Memcached on WordPress</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DoNngKYmgMkKBsXzexKKEQ.png" /><figcaption>Setting up WP Offload Media Lite Plugin to Offload Static Content to S3</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*HQGCoEfutWlpRgLFVda5gg.png" /><figcaption>Uploading a File</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*lgpP_ImY_pnpXwUkfWrMkw.png" /><figcaption>Uploaded File Successfully with S3 as Destination Bucket</figcaption></figure><p><strong>Conclusion: </strong>Incorporating Terraform into the process of building a highly available WordPress application on AWS further enhances efficiency and consistency. With Terraform’s infrastructure as code approach, developers can define and manage the entire AWS environment, including EC2 instances, RDS databases, CloudFront distributions, and Auto Scaling configurations, using simple and repeatable code. This enables rapid deployment, easy replication across multiple environments, and streamlined maintenance of the infrastructure. By abstracting away the manual configuration steps and automating the provisioning process, Terraform ensures that the infrastructure is consistently deployed in a highly available configuration, minimizing human error and enabling seamless scalability.</p><p>This code showcases a basic implementation of Terraform as an IaC tool. Terraform is powerful and to further improve this code, community modules and the DRY principle may be implemented.</p><p><strong>Thank you for taking the time to read my blog!</strong> If you have any questions or comments, I would love to hear from you. Please feel free to reach out to me through any of the following channels:</p><p>Email: <strong>cloudsenseing@gmail.com</strong><br>Social media: <a href="https://linkedin.com/in/mayowa-bodunwa"><strong>LinkedIn</strong></a><strong> </strong>or <a href="https://twitter.com/mayowabodunwa"><strong>Twitter</strong></a></p><p>I am passionate about the topics I write about, and I am always eager to engage with my readers. If you have a suggestion for a future blog post, or if you would like to collaborate on a project, please do not hesitate to get in touch. I will do my best to respond to all inquiries as quickly as possible.</p><p><strong>References:</strong></p><ul><li>AWS Terraform Provider Guide: <a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs">https://registry.terraform.io/providers/hashicorp/aws/latest/docs</a></li><li>Reference Architecture — Best Practices for WordPress on AWS: <a href="https://docs.aws.amazon.com/whitepapers/latest/best-practices-wordpress/reference-architecture.html">https://docs.aws.amazon.com/whitepapers/latest/best-practices-wordpress/reference-architecture.html</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=408346c39dfe" width="1" height="1" alt=""><hr><p><a href="https://towardsaws.com/cloud-project-1-deploying-a-highly-available-wordpress-architecture-on-aws-with-terraform-408346c39dfe">Cloud Project 1: Deploying a Highly Available WordPress Architecture on AWS with Terraform</a> was originally published in <a href="https://towardsaws.com">Towards AWS</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>