<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: THARUN REDDY R</title>
    <description>The latest articles on DEV Community by THARUN REDDY R (@tharun_reddy_14).</description>
    <link>https://dev.to/tharun_reddy_14</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2604640%2Fb6da4d8e-6510-47da-914a-317a7182625d.jpg</url>
      <title>DEV Community: THARUN REDDY R</title>
      <link>https://dev.to/tharun_reddy_14</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tharun_reddy_14"/>
    <language>en</language>
    <item>
      <title>Automating a Multi-Tier Web App on AWS with Terraform and Ansible: Triumphs and Challenges</title>
      <dc:creator>THARUN REDDY R</dc:creator>
      <pubDate>Tue, 03 Jun 2025 16:22:34 +0000</pubDate>
      <link>https://dev.to/tharun_reddy_14/automating-a-multi-tier-web-app-on-aws-with-terraform-and-ansible-triumphs-and-challenges-1bmm</link>
      <guid>https://dev.to/tharun_reddy_14/automating-a-multi-tier-web-app-on-aws-with-terraform-and-ansible-triumphs-and-challenges-1bmm</guid>
      <description>&lt;p&gt;As part of my journey to master cloud computing and DevOps, I recently tackled an advanced project: deploying a fully automated multi-tier web application on AWS using Terraform for infrastructure provisioning and Ansible for server configuration. This project built on my previous experience with AWS, pushing me to embrace infrastructure as code (IaC) and automation. Despite numerous challenges, including a critical misconfiguration in my Terraform variables, I successfully deployed a secure, monitored web application. In this blog post, I’ll share what I accomplished, the hurdles I faced, how I overcame them, and the lessons that will shape my future projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Objectives
&lt;/h2&gt;

&lt;p&gt;The goal was to create a scalable, secure multi-tier web application on AWS with the following components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Terraform:&lt;/strong&gt; Provision a Virtual Private Cloud (VPC), public and private subnets, EC2 instances, security groups, and IAM roles.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ansible:&lt;/strong&gt; Configure a web server with Nginx and PHP, and a database server with MariaDB, deploying a PHP application that connects to the database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhancements:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Set up AWS CloudWatch to monitor CPU usage.&lt;/li&gt;
&lt;li&gt;Store Terraform state remotely in an S3 bucket for consistency.&lt;/li&gt;
&lt;li&gt;Implement a unit test to verify the web application’s functionality.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The final deliverable was a working web application accessible via the web server’s public IP, with the database securely isolated in a private subnet, all provisioned and configured automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-Step Accomplishments
&lt;/h2&gt;

&lt;p&gt;Here’s what I achieved, broken down into key phases, along with the challenges I encountered and how I resolved them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Setting Up the Project Environment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I started by organizing my project directory (&lt;code&gt;project4/&lt;/code&gt;) with subfolders for Terraform (&lt;code&gt;terraform/&lt;/code&gt;), Ansible (&lt;code&gt;ansible/&lt;/code&gt;), and scripts (&lt;code&gt;scripts/&lt;/code&gt;). I installed Terraform, Ansible, and the AWS CLI on my local machine, ensuring my AWS credentials were configured correctly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accomplishment:&lt;/strong&gt; Established a clean, structured project environment to support IaC and automation workflows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Provisioning Infrastructure with Terraform&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using Terraform, I provisioned a robust AWS infrastructure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;VPC:&lt;/strong&gt; Created &lt;code&gt;multi-tier-vpc&lt;/code&gt; with a CIDR block of &lt;code&gt;10.0.0.0/16&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subnets:&lt;/strong&gt; Set up a public subnet (&lt;code&gt;10.0.1.0/24&lt;/code&gt;) for the web server and a private subnet (&lt;code&gt;10.0.2.0/24&lt;/code&gt;) for the database server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Networking:&lt;/strong&gt; Configured an Internet Gateway for public subnet access, a NAT Gateway for private subnet outbound traffic, and route tables to manage traffic flow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Groups:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;web-sg&lt;/code&gt;: Allowed HTTP (port 80) from anywhere and SSH (port 22) from my IP.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;db-sg&lt;/code&gt;: Permitted MariaDB (port 3306) and SSH (port 22) from the web server’s security group.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;IAM Role:&lt;/strong&gt; Granted EC2 instances S3 read-only access via an instance profile.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;EC2 Instances:&lt;/strong&gt; Launched a web server in the public subnet and a database server (IP: &lt;code&gt;10.0.2.219&lt;/code&gt;) in the private subnet, both using Amazon Linux 2023.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Outputs:&lt;/strong&gt; Exposed the web server’s public IP and database server’s private IP for Ansible.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Challenge:&lt;/strong&gt; When creating the &lt;code&gt;variables.tf&lt;/code&gt; file, I mistakenly defined the &lt;code&gt;web_sg_ingress&lt;/code&gt; variable with port 80 twice instead of including port 22 for SSH:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;web_sg_ingress&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"MY_IP/32"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;# Error: Should be port 22&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This caused SSH connection failures to the web server, as port 22 was blocked. I spent hours troubleshooting, expecting a key pair or network issue, only to realize the security group misconfiguration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; I updated &lt;code&gt;variables.tf&lt;/code&gt; to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;web_sg_ingress&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;from_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;to_port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;protocol&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"MY_IP/32"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reapplied Terraform (&lt;code&gt;terraform apply&lt;/code&gt;), and SSH access was restored.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accomplishment:&lt;/strong&gt; Successfully provisioned a secure, multi-tier AWS infrastructure using Terraform, learning the importance of double-checking security group configurations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Configuring Servers with Ansible&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I used Ansible to automate server setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Web Server:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installed Nginx, PHP, and PHP-FPM.&lt;/li&gt;
&lt;li&gt;Deployed &lt;code&gt;index.php&lt;/code&gt;, a PHP script that connects to the database and displays "Connected successfully to the database!".&lt;/li&gt;
&lt;li&gt;Configured Nginx to serve PHP files.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Database Server:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Installed MariaDB, created a database (&lt;code&gt;my_database&lt;/code&gt;), and set up a user (&lt;code&gt;web_user&lt;/code&gt;) with access from the web server.&lt;/li&gt;
&lt;li&gt;Secured MariaDB with a root password.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Challenge:&lt;/strong&gt; The initial &lt;code&gt;db_playbook.yml&lt;/code&gt; failed because &lt;code&gt;mariadb-server&lt;/code&gt; wasn’t available on Amazon Linux 2023:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;No package mariadb-server available.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AL2023 doesn’t use &lt;code&gt;amazon-linux-extras&lt;/code&gt; (unlike Amazon Linux 2), and the correct package was &lt;code&gt;mariadb105-server&lt;/code&gt;. Additionally, I used &lt;code&gt;yum&lt;/code&gt; instead of &lt;code&gt;dnf&lt;/code&gt;, AL2023’s default package manager.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Updated the playbook to use &lt;code&gt;dnf&lt;/code&gt; and install &lt;code&gt;mariadb105-server&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install MariaDB server&lt;/span&gt;
  &lt;span class="na"&gt;dnf&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mariadb105-server&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;present&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Challenge:&lt;/strong&gt; An earlier playbook run failed on the &lt;code&gt;mysql_user&lt;/code&gt; task to set the MariaDB root password, likely due to AL2023’s default &lt;code&gt;unix_socket&lt;/code&gt; authentication for the root user.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Modified the playbook to handle initial root access without a password and ignore errors for fresh installs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set MariaDB root password for the first time&lt;/span&gt;
  &lt;span class="na"&gt;mysql_user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
    &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;mysql_root_password&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
    &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;
    &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;present&lt;/span&gt;
    &lt;span class="na"&gt;login_user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
    &lt;span class="na"&gt;login_password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
  &lt;span class="na"&gt;no_log&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;ignore_errors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Accomplishment:&lt;/strong&gt; Automated server configuration with Ansible, overcoming package and authentication issues specific to AL2023.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Adding CloudWatch Monitoring&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I configured AWS CloudWatch to monitor CPU utilization for both EC2 instances:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Created alarms (&lt;code&gt;web-cpu-alarm&lt;/code&gt; and &lt;code&gt;db-cpu-alarm&lt;/code&gt;) to trigger if CPU usage exceeds 80% for 4 minutes.&lt;/li&gt;
&lt;li&gt;Integrated this into &lt;code&gt;main.tf&lt;/code&gt; with Terraform.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Accomplishment:&lt;/strong&gt; Implemented basic monitoring to ensure infrastructure health, enhancing the project’s production readiness.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Configuring S3 Backend for Terraform State&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To ensure state consistency, I stored Terraform’s state file in an S3 bucket (&lt;code&gt;terraform-state-bucket&lt;/code&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Created the bucket manually in the AWS Console.&lt;/li&gt;
&lt;li&gt;Updated &lt;code&gt;main.tf&lt;/code&gt; with:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;"s3"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-state-bucket"&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"project4/terraform.tfstate"&lt;/span&gt;
    &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Reinitialized Terraform (&lt;code&gt;terraform init&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Accomplishment:&lt;/strong&gt; Enabled remote state management, a best practice for collaborative IaC projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. Implementing a Unit Test&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I wrote a Bash script (&lt;code&gt;test.sh&lt;/code&gt;) to verify the web application:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetched the web server’s public IP from Terraform output.&lt;/li&gt;
&lt;li&gt;Used &lt;code&gt;curl&lt;/code&gt; to check if &lt;code&gt;http://&amp;lt;web_public_ip&amp;gt;/index.php&lt;/code&gt; returned "Connected successfully to the database!".&lt;/li&gt;
&lt;li&gt;Output "Test passed" or "Test failed" based on the response.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Accomplishment:&lt;/strong&gt; Added automated testing to validate application functionality, ensuring reliability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. Cleaning Up Resources&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Learning from my previous project’s $27 bill due to lingering resources, I destroyed all AWS resources after completion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform destroy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This removed the VPC, EC2 instances, NAT Gateway, and other components, avoiding unexpected charges.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accomplishment:&lt;/strong&gt; Practiced responsible cloud management, ensuring no cost overruns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;This project was a significant step forward in my DevOps journey, but it wasn’t without challenges. Key takeaways include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Attention to Detail in IaC:&lt;/strong&gt; The variables.tf error (port 80 instead of 22) taught me to meticulously review configurations, especially for security groups, as small mistakes can cause significant issues.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OS-Specific Considerations:&lt;/strong&gt; AL2023’s differences (e.g., dnf vs. yum, mariadb105-server vs. mariadb-server) highlighted the importance of understanding the target operating system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automation Saves Time:&lt;/strong&gt; Ansible and Terraform reduced manual setup, but debugging automation scripts requires patience and log analysis.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost Awareness:&lt;/strong&gt; Destroying resources immediately after use is critical to avoid charges, a lesson reinforced by my previous project’s mistake.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistence Pays Off:&lt;/strong&gt; Each challenge, from SSH failures to package errors, was a chance to deepen my understanding of AWS and automation tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;With this project complete, I’m excited to explore more advanced DevOps concepts, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implementing auto-scaling groups with Terraform.&lt;/li&gt;
&lt;li&gt;Using Ansible roles for more modular playbooks.&lt;/li&gt;
&lt;li&gt;Adding CI/CD pipelines to automate deployments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This project has equipped me with the skills to tackle these challenges with confidence. Stay tuned for my next adventure in cloud computing!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Deploying a fully automated multi-tier web app on AWS with Terraform and Ansible was both challenging and rewarding. From misconfiguring security groups to navigating AL2023’s package quirks, I faced obstacles that tested my problem-solving skills. Yet, the result—a secure, monitored, and tested web application—was worth the effort. This project not only solidified my understanding of IaC and automation but also taught me the importance of precision and cost management in the cloud. If you’re diving into a similar project, embrace the challenges—they’re your best teachers. Happy automating!&lt;/p&gt;

</description>
      <category>terraform</category>
      <category>ansible</category>
      <category>devops</category>
      <category>aws</category>
    </item>
    <item>
      <title>Deploying a Multi-Tier Web App on AWS: A Journey Through Challenges and Costly Lesson</title>
      <dc:creator>THARUN REDDY R</dc:creator>
      <pubDate>Sat, 24 May 2025 12:22:51 +0000</pubDate>
      <link>https://dev.to/tharun_reddy_14/deploying-a-multi-tier-web-app-on-aws-a-journey-through-challenges-and-costly-lesson-4m44</link>
      <guid>https://dev.to/tharun_reddy_14/deploying-a-multi-tier-web-app-on-aws-a-journey-through-challenges-and-costly-lesson-4m44</guid>
      <description>&lt;p&gt;As part of my journey to master cloud computing and DevOps, I recently completed an intermediate-level project: deploying a multi-tier web application on AWS. The goal was to build a secure, scalable architecture with a web server in a public subnet and a database server in a private subnet, using AWS services like EC2, VPC, and security groups. While the project was a success, it came with significant hurdles—both technical and financial. In this blog post, I’ll walk you through the project’s objectives, the numerous issues I faced, how I resolved them, and a costly lesson about AWS resource cleanup that left me with a $27 bill.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Objectives
&lt;/h2&gt;

&lt;p&gt;The project had clear goals to test my AWS and Linux skills:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Launch EC2 Instances:&lt;/strong&gt; Set up one web server and one database server using Amazon EC2.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configure Nginx:&lt;/strong&gt; Use Nginx as a reverse proxy on the web server to serve a PHP-based web application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automate with Ansible:&lt;/strong&gt; Provision both servers using Ansible playbooks for consistency (though I ended up skipping this due to challenges).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enhance Security:&lt;/strong&gt; Create a Virtual Private Cloud (VPC) with public and private subnets, placing the database in the private subnet to protect it from direct internet access.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The final deliverable was a working web application accessible via the web server’s public IP, with the backend database securely tucked away in a private subnet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step-by-Step Implementation
&lt;/h2&gt;

&lt;p&gt;Here’s how I built the project, broken down into key phases, along with the issues I faced and how I tackled them.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Setting Up the VPC and Networking&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;To create a secure architecture, I started by configuring a custom VPC (&lt;code&gt;multi-tier-vpc&lt;/code&gt;) with:&lt;/li&gt;
&lt;li&gt;Public Subnet (&lt;code&gt;10.0.1.0/24&lt;/code&gt;) for the web server.&lt;/li&gt;
&lt;li&gt;Private Subnet (&lt;code&gt;10.0.2.0/24&lt;/code&gt;) for the database server.&lt;/li&gt;
&lt;li&gt;An Internet Gateway to allow the public subnet to access the internet.&lt;/li&gt;
&lt;li&gt;Route Tables to manage traffic, with the public subnet routing to the Internet Gateway.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Issue: The database server, in the private subnet, couldn’t access the internet to fetch package updates, causing errors like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Could not retrieve mirrorlist https://amazonlinux-2-repos-eu-north-1.s3.dualstack.eu-north-1.amazonaws.com/... Timeout was reached
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: I added a NAT Gateway in the public subnet and updated the private subnet’s route table to route &lt;code&gt;0.0.0.0/0&lt;/code&gt; traffic through it. This allowed the database server to download packages while remaining isolated from inbound traffic.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Launching EC2 Instances
I launched two EC2 instances using the Amazon Linux 2 AMI:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Web Server:&lt;/strong&gt; Placed in the public subnet (&lt;code&gt;10.0.1.37&lt;/code&gt;, public IP: &lt;code&gt;51.20.138.105&lt;/code&gt;), with a security group (&lt;code&gt;web-sg&lt;/code&gt;) allowing HTTP (port 80) and SSH (port 22).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database Server:&lt;/strong&gt; Placed in the private subnet (&lt;code&gt;10.0.2.129&lt;/code&gt;), with a security group (&lt;code&gt;db-sg&lt;/code&gt;) allowing MariaDB (port 3306) and SSH (port 22) from the web server’s security group.&lt;/li&gt;
&lt;li&gt;Both used the same SSH key pair (&lt;code&gt;multi-tier-key&lt;/code&gt;) for access.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; I couldn’t SSH into the database server from the web server, getting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Permission denied (publickey,gssapi-keyex,gssapi-with-mic).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; The web server didn’t have the private key. I used &lt;code&gt;scp&lt;/code&gt; to copy &lt;code&gt;multi-tier-key.pem&lt;/code&gt; to the web server’s &lt;code&gt;~/.ssh/&lt;/code&gt; directory and set permissions (&lt;code&gt;chmod 400&lt;/code&gt;). I also verified the database server’s &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt; file contained the correct public key, using AWS Systems Manager Session Manager to access it initially.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Configuring the Database Server
I intended to install MySQL on the database server, but ran into an issue.
Issue: The command &lt;code&gt;sudo yum install mysql-server -y&lt;/code&gt; failed with:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;No package mysql-server available.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Amazon Linux 2 uses MariaDB as a drop-in replacement for MySQL. I installed &lt;code&gt;mariadb-server&lt;/code&gt; instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;mariadb-server &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start mariadb
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;mariadb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I secured MariaDB with &lt;code&gt;mysql_secure_installation&lt;/code&gt;, setting a root password and restricting root access to &lt;code&gt;localhost&lt;/code&gt; for security.&lt;/p&gt;

&lt;p&gt;Issue: The web server couldn’t connect to the database, with &lt;code&gt;telnet 10.0.2.129 3306&lt;/code&gt; showing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host '10.0.1.37' is not allowed to connect to this MariaDB server.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; MariaDB’s user permissions didn’t allow connections from the web server’s IP. I created a user (&lt;code&gt;web_user&lt;/code&gt;) with access from &lt;code&gt;10.0.1.37&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;USER&lt;/span&gt; &lt;span class="s1"&gt;'web_user'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'10.0.1.37'&lt;/span&gt; &lt;span class="n"&gt;IDENTIFIED&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="s1"&gt;'your_password'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;GRANT&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt; &lt;span class="k"&gt;PRIVILEGES&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;my_database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;TO&lt;/span&gt; &lt;span class="s1"&gt;'web_user'&lt;/span&gt;&lt;span class="o"&gt;@&lt;/span&gt;&lt;span class="s1"&gt;'10.0.1.37'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;FLUSH&lt;/span&gt; &lt;span class="k"&gt;PRIVILEGES&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Setting Up the Web Server
I installed Nginx and PHP on the web server to serve a simple PHP script (&lt;code&gt;index.php&lt;/code&gt;) that connected to the database.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Issue: Running &lt;code&gt;telnet 10.0.2.129 3306&lt;/code&gt; initially failed with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-bash: telnet: command not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; I installed the &lt;code&gt;telnet&lt;/code&gt; package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Solution: I installed the telnet package:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Issue: Accessing &lt;code&gt;http://51.20.138.105/index.php&lt;/code&gt; resulted in a &lt;code&gt;502 Bad Gateway&lt;/code&gt; error. Solution: This was a multi-faceted issue:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PHP-FPM Not Installed: The &lt;code&gt;php-fpm&lt;/code&gt; service was missing, causing Nginx to fail when processing PHP files. I installed it:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;php-fpm &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl start php-fpm
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;php-fpm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Nginx Configuration Error: The &lt;code&gt;/etc/nginx/conf.d/php.conf&lt;/code&gt; file had a syntax error (&lt;code&gt;10.0.2.129 localhost;&lt;/code&gt;) and a misplaced &lt;code&gt;location&lt;/code&gt; block. I corrected it to:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    listen 80;
    server_name _;
    root /usr/share/nginx/html;
    index index.php index.html index.htm;
    location / {
        try_files $uri $uri/ =404;
    }
    location ~ \.php$ {
        fastcgi_pass unix:/run/php-fpm/www.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Socket Mismatch: Nginx was configured for &lt;code&gt;/var/run/php-fpm/php-fpm.sock&lt;/code&gt;, but PHP-FPM used &lt;code&gt;/run/php-fpm/www.sock&lt;/code&gt;. I updated &lt;code&gt;fastcgi_pass&lt;/code&gt; to match after checking &lt;code&gt;/etc/php-fpm.d/www.conf&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Issue:&lt;/strong&gt; The &lt;code&gt;index.php&lt;/code&gt; file wasn’t initially on the web server. Solution: I used &lt;code&gt;scp&lt;/code&gt; to upload it from my local machine to &lt;code&gt;/usr/share/nginx/html/&lt;/code&gt; and set permissions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo chown &lt;/span&gt;nginx:nginx /usr/share/nginx/html/index.php
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;644 /usr/share/nginx/html/index.php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Final Testing
After resolving these issues, I tested the application:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Visited &lt;code&gt;http://51.20.138.105/index.php&lt;/code&gt; and saw: "Connected successfully to the database!"&lt;/li&gt;
&lt;li&gt;Verified SSH access to the database server via the web server:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/multi-tier-key.pem ec2-user@10.0.2.129
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;The Costly Mistake: Leaving Resources Running&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After completing the project, I stopped my EC2 instances and left them idle for over two weeks, assuming that since I was using the AWS Free Tier, I wouldn’t incur charges. To my shock, I received a pending AWS bill for $27! It turned out that while the EC2 instances were stopped, other resources like the VPC, NAT Gateway, and associated components (e.g., Elastic IPs) were still active and accruing costs. NAT Gateways, in particular, are not covered by the Free Tier and can be surprisingly expensive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; I immediately terminated all resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EC2 Instances: Deleted both the web and database servers via the EC2 Console.&lt;/li&gt;
&lt;li&gt;VPC: Removed the &lt;code&gt;multi-tier-vpc&lt;/code&gt;, along with subnets, route tables, NAT Gateway, and Internet Gateway.&lt;/li&gt;
&lt;li&gt;Security Groups and Key Pairs: Ensured no lingering resources remained.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This experience taught me the critical importance of fully cleaning up all AWS services after a project is complete, not just stopping instances. I learned this lesson the hard way, but it’s one I won’t forget.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Missed
&lt;/h2&gt;

&lt;p&gt;The original plan included using Ansible to automate server provisioning. However, due to the numerous issues (network timeouts, package errors, configuration mismatches), I opted for manual setup. While this meant more hands-on troubleshooting, it deepened my understanding of AWS and Linux. I plan to incorporate Ansible in my next project to streamline automation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lessons Learned
&lt;/h2&gt;

&lt;p&gt;This project was a rollercoaster, but it taught me invaluable lessons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS Networking is Critical: Understanding VPCs, subnets, NAT Gateways, and security groups is essential for secure cloud architectures.&lt;/li&gt;
&lt;li&gt;Debugging is a Skill: Logs (&lt;code&gt;/var/log/nginx/error.log&lt;/code&gt;, &lt;code&gt;/var/log/php-fpm/error.log&lt;/code&gt;) were my best friends for diagnosing issues like socket mismatches.&lt;/li&gt;
&lt;li&gt;Security Matters: Placing the database in a private subnet and restricting MariaDB access to specific IPs significantly enhanced security.&lt;/li&gt;
&lt;li&gt;Clean Up Resources: Always terminate all AWS resources (not just EC2 instances) to avoid unexpected charges. My $27 bill was a painful but crucial lesson in cloud cost management.&lt;/li&gt;
&lt;li&gt;Persistence Pays Off: Each issue felt daunting, but breaking them down and tackling them systematically led to success.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;I’m now diving into an Advanced Project: Fully Automated AWS Infrastructure with Terraform &amp;amp; Ansible. This will involve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using Terraform to provision AWS resources (VPC, EC2, IAM roles).&lt;/li&gt;
&lt;li&gt;Configuring servers with Ansible for a scalable web app.&lt;/li&gt;
&lt;li&gt;Adding CloudWatch monitoring, S3 state storage, and unit tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The struggles from this project, especially the cost oversight, have prepared me to appreciate automation tools like Terraform and Ansible, which should reduce manual errors and streamline setup. I’ll also be vigilant about cleaning up resources to avoid another surprise bill!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Deploying a multi-tier web app on AWS was challenging but rewarding. From network timeouts to Nginx configuration woes and an unexpected $27 bill, I faced a steep learning curve but emerged with a functional application and a deeper understanding of cloud infrastructure. The financial lesson about resource cleanup was a hard one, but it underscored the importance of diligence in cloud management. If you’re embarking on a similar project, don’t be discouraged by setbacks—each issue is a chance to learn, and always double-check your AWS resources before walking away. Happy cloud computing!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Automating My EC2 with Ansible: A Tale of WSL Woes and Nginx Triumphs</title>
      <dc:creator>THARUN REDDY R</dc:creator>
      <pubDate>Mon, 31 Mar 2025 11:07:04 +0000</pubDate>
      <link>https://dev.to/tharun_reddy_14/automating-my-ec2-with-ansible-a-tale-of-wsl-woes-and-nginx-triumphs-46n</link>
      <guid>https://dev.to/tharun_reddy_14/automating-my-ec2-with-ansible-a-tale-of-wsl-woes-and-nginx-triumphs-46n</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;For Project 3 in my DevOps journey, I set out to automate an EC2 instance setup with Ansible—installing Nginx and securing it by disabling root SSH login. What seemed like a straightforward task turned into a learning adventure, especially since I’m on Windows and had to wrestle with WSL quirks. Here’s how I pulled it off, stumbles and all.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Plan
&lt;/h2&gt;

&lt;p&gt;My goal was clear: use Ansible to configure an EC2 instance efficiently. The steps were:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install Ansible on my local machine.&lt;/li&gt;
&lt;li&gt;Launch a fresh Amazon Linux 2 EC2 instance with SSH access.&lt;/li&gt;
&lt;li&gt;Set up an Ansible inventory file to connect to the instance.&lt;/li&gt;
&lt;li&gt;Write a playbook to install Nginx and disable root SSH login.&lt;/li&gt;
&lt;li&gt;Run the playbook and verify the automation worked — Nginx running, root access blocked.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How It Went
&lt;/h2&gt;

&lt;p&gt;Here’s what I did to make it happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Installing Ansible:&lt;/strong&gt; Since I’m on Windows, I installed WSL (Windows Subsystem for Linux) and Ubuntu. Then, I ran:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  &lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
  &lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;ansible
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ansible was ready after confirming with &lt;code&gt;ansible --version.&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New EC2 Instance: I created an Amazon Linux 2 instance in AWS with a &lt;code&gt;t3.micro&lt;/code&gt;, a security group for SSH (port 22, my IP), and a key pair (&lt;code&gt;my-ansible-key.pem&lt;/code&gt;). SSH worked fine:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; my-ansible-key.pem ec2-user@13.60.41.232
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Inventory Setup: I created &lt;code&gt;inventory.ini&lt;/code&gt; with my instance’s IP and key path, but testing the connection hit a snag (more on that later).&lt;/li&gt;
&lt;li&gt;Playbook Creation: I wrote &lt;code&gt;setup.yml&lt;/code&gt; to install Nginx and disable root SSH, but running it revealed more hurdles.&lt;/li&gt;
&lt;li&gt;Verification: After tweaks, I confirmed Nginx at &lt;code&gt;http://&amp;lt;ec2-public-ip&amp;gt;&lt;/code&gt; and tested SSH—root was denied, &lt;code&gt;ec2-user&lt;/code&gt; worked.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Challenges &amp;amp; Solutions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Challenge 1: WSL and Inventory File Permissions
&lt;/h3&gt;

&lt;p&gt;When I ran &lt;code&gt;ansible -m ping -i inventory.ini ec2&lt;/code&gt;, I got errors about parsing &lt;code&gt;inventory.ini&lt;/code&gt; and an "unprotected private key" warning. The issue? My &lt;code&gt;.pem&lt;/code&gt; file was in &lt;code&gt;/mnt/c/...&lt;/code&gt; (Windows file system), and WSL couldn’t enforce proper permissions (&lt;code&gt;0555&lt;/code&gt; was too open).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Solution: I copied the key to /root/my-ansible-key.pem in WSL:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cp&lt;/span&gt; /mnt/c/Users/deepa/downloads/devops/project2/my-ansible-key.pem /root/my-ansible-key.pem
&lt;span class="nb"&gt;chmod &lt;/span&gt;0600 /root/my-ansible-key.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Updated &lt;code&gt;inventory.ini&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;ec2]
13.60.41.232 &lt;span class="nv"&gt;ansible_user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ec2-user &lt;span class="nv"&gt;ansible_ssh_private_key_file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/root/my-ansible-key.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ping worked: &lt;code&gt;pong&lt;/code&gt;!&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 2: Nginx Not Found in Playbook
&lt;/h3&gt;

&lt;p&gt;Running &lt;code&gt;ansible-playbook -i inventory.ini setup.yml&lt;/code&gt; failed with "No package matching 'nginx' found." Amazon Linux 2 doesn’t include Nginx in its default repos—I needed &lt;code&gt;amazon-linux-extras&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Solution: I tweaked &lt;code&gt;setup.yml&lt;/code&gt; to enable Nginx first.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;- name: Enable Nginx &lt;span class="k"&gt;in &lt;/span&gt;amazon-linux-extras
  &lt;span class="nb"&gt;command&lt;/span&gt;: amazon-linux-extras &lt;span class="nb"&gt;enable &lt;/span&gt;nginx1
  args:
    creates: /var/lib/amazon-linux-extras/nginx1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reran the playbook, and Nginx installed successfully.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 3: No HTTP Access
&lt;/h3&gt;

&lt;p&gt;After Nginx installed, &lt;code&gt;http://&amp;lt;ec2-public-ip&amp;gt;&lt;/code&gt; didn’t load—my security group only allowed SSH (port 22), not HTTP (port 80).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Solution: Added an inbound rule in AWS:&lt;/li&gt;
&lt;li&gt;Type: HTTP&lt;/li&gt;
&lt;li&gt;Port: 80&lt;/li&gt;
&lt;li&gt;Source: 0.0.0.0/0 (for testing) The Nginx welcome page appeared!&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;WSL Workarounds: Moving files to WSL’s file system avoids permission headaches with Windows paths.&lt;/li&gt;
&lt;li&gt;Package Dependencies: Automation needs to account for OS-specific repos — &lt;code&gt;amazon-linux-extras&lt;/code&gt; was my savior.&lt;/li&gt;
&lt;li&gt;Security Groups Are Key: Forgetting HTTP access reminded me to align network rules with app needs.&lt;/li&gt;
&lt;li&gt;Automation Wins: Seeing Nginx pop up and root SSH get blocked with one command felt like magic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;This project was a grind, but it taught me how Ansible simplifies server setup—and how to troubleshoot when things go sideways. What’s your go-to fix for Ansible hiccups?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>cloud</category>
      <category>linux</category>
      <category>aws</category>
    </item>
    <item>
      <title>Securing My AWS EC2: A Journey Through IAM and SSH Struggles</title>
      <dc:creator>THARUN REDDY R</dc:creator>
      <pubDate>Mon, 31 Mar 2025 08:46:35 +0000</pubDate>
      <link>https://dev.to/tharun_reddy_14/securing-my-aws-ec2-a-journey-through-iam-and-ssh-struggles-3e9d</link>
      <guid>https://dev.to/tharun_reddy_14/securing-my-aws-ec2-a-journey-through-iam-and-ssh-struggles-3e9d</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;After getting my first EC2 instance up with Nginx, I dove into Project 2 of my DevOps roadmap: setting up IAM users and roles, and securing an EC2 instance with a security group. My goal? Create a user with limited access, attach a role for S3 permissions, and test it all out. What started as a straightforward task turned into a rollercoaster of SSH woes—but I came out stronger. Here’s how it unfolded.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Plan
&lt;/h2&gt;

&lt;p&gt;The project had clear steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Create an IAM User:&lt;/strong&gt; Set up &lt;code&gt;devops-user1&lt;/code&gt; with &lt;code&gt;AmazonEC2ReadOnlyAccess&lt;/code&gt; for console and programmatic access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create an IAM Role:&lt;/strong&gt; Build &lt;code&gt;ec2-s3-role&lt;/code&gt; with &lt;code&gt;AmazonS3ReadOnlyAccess&lt;/code&gt; for my EC2 instance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configure Security:&lt;/strong&gt; Define a security group with SSH (port 22) restricted to my IP and generate a key pair (&lt;code&gt;my-new-key&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Launch an Instance:&lt;/strong&gt; Spin up an Amazon Linux 2 instance with the security group and attach the role.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test It:&lt;/strong&gt; Log in as &lt;code&gt;devops-user1&lt;/code&gt; to check EC2 read-only access, and SSH into the instance to run &lt;code&gt;aws s3 ls&lt;/code&gt; using the role.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Simple, right? Well, not quite.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Went
&lt;/h2&gt;

&lt;p&gt;I kicked off in the AWS Console:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;IAM User:&lt;/strong&gt; Created &lt;code&gt;devops-user1&lt;/code&gt;, downloaded the access keys, and logged in to confirm read-only EC2 access worked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM Role:&lt;/strong&gt; Set up &lt;code&gt;ec2-s3-role&lt;/code&gt;, attached it to my instance via &lt;strong&gt;EC2 &amp;gt; Actions &amp;gt; Modify IAM role&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Group:&lt;/strong&gt; Added SSH (port 22, my IP) and launched the instance with Amazon Linux 2 AMI and my new key pair.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The instance launched fine, and I could connect via the AWS Console’s EC2 Instance Connect. But when I tried SSH from my local system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; my-new-key.pem ec2-user@&amp;lt;public-ip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It timed out. I was stumped—I’d followed all the steps!&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges &amp;amp; Solutions
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Challenge 1: SSH Port 22 Timeout
&lt;/h4&gt;

&lt;p&gt;After launching the instance, I couldn’t SSH from my local machine—it just kept timing out with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh: connect to host &amp;lt;public-ip&amp;gt; port 22: Connection timed out
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The security group was set to allow port 22 from anywhere, and the instance was running. I even tested as devops-user1 with the access keys via AWS CLI, but SSH refused to budge.&lt;/p&gt;

&lt;h4&gt;
  
  
  Challenge 2: Switching to Port 2222 Didn’t Help
&lt;/h4&gt;

&lt;p&gt;Thinking my network might be blocking port 22, I edited &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt; on the instance (via Console access) to use port 2222, restarted &lt;code&gt;sshd&lt;/code&gt;, and updated the security group. Tried again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; my-new-key.pem &lt;span class="nt"&gt;-p&lt;/span&gt; 2222 ec2-user@&amp;lt;public-ip&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Still no luck—now it said "Connection refused." Telnet (&lt;code&gt;telnet &amp;lt;public-ip&amp;gt; 2222&lt;/code&gt;) confirmed the connection failed too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution: Fresh Start with a New Instance
&lt;/h2&gt;

&lt;p&gt;Frustrated, I decided to wipe the slate clean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terminated the instance, deleted the security group, and trashed the old key pair.&lt;/li&gt;
&lt;li&gt;Launched a new instance from scratch:&lt;/li&gt;
&lt;li&gt;Amazon Linux 2 AMI, &lt;code&gt;t3.micro&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;New security group with SSH (port 22, anywhere).&lt;/li&gt;
&lt;li&gt;Fresh key pair (&lt;code&gt;my-new-key&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This time, SSH worked like a charm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; my-new-key.pem ec2-user@&amp;lt;public-ip&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Opened Git bash &amp;amp; ran &lt;code&gt;aws s3 ls&lt;/code&gt; —it listed my buckets, proving the &lt;code&gt;ec2-s3-role&lt;/code&gt; worked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Double-Check Everything:&lt;/strong&gt; My first instance’s woes might’ve stemmed from a misconfigured subnet or lingering settings—starting fresh ruled those out.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network Matters:&lt;/strong&gt; Port 22 might’ve been blocked locally; testing from another network could’ve confirmed it, but the reset worked too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM Power:&lt;/strong&gt; Roles are a game-changer—no credentials on the instance, just seamless S3 access.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistence Pays:&lt;/strong&gt; Troubleshooting taught me more than a smooth run would’ve.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Now that SSH and IAM are in my toolkit, I’m ready for the next challenge. How do you debug SSH woes—any tricks up your sleeve?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>cloud</category>
      <category>aws</category>
      <category>security</category>
    </item>
    <item>
      <title>My First AWS EC2 Instance: From Stumbles to a Running Nginx Server</title>
      <dc:creator>THARUN REDDY R</dc:creator>
      <pubDate>Sun, 23 Mar 2025 13:29:52 +0000</pubDate>
      <link>https://dev.to/tharun_reddy_14/my-first-aws-ec2-instance-from-stumbles-to-a-running-nginx-server-2pad</link>
      <guid>https://dev.to/tharun_reddy_14/my-first-aws-ec2-instance-from-stumbles-to-a-running-nginx-server-2pad</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Starting my DevOps journey, I set out to launch an EC2 instance and host a web server. It wasn’t all smooth sailing, but I learned a ton along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Plan
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Launch an EC2 instance with AWS Console and CLI.&lt;/li&gt;
&lt;li&gt;Connect via SSH.&lt;/li&gt;
&lt;li&gt;Set up an Nginx web server.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How It Went
&lt;/h2&gt;

&lt;p&gt;I kicked off by launching an instance via the AWS Console—easy enough with Amazon Linux 2 and a &lt;code&gt;t3.micro&lt;/code&gt;. I added SSH (port 22) and HTTP (port 80) to the security group, grabbed my key pair, and SSH’d in with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ssh -i my-key.pem ec2-user@&amp;lt;public-ip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, I ran:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo yum update -y
sudo amazon-linux-extras install nginx1 -y
sudo systemctl start nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But then, &lt;em&gt;I hit a snag.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge 1: Wrong AMI, Wrong Tools
&lt;/h2&gt;

&lt;p&gt;My first attempt flopped—I couldn’t install Nginx because amazon-linux-extras wasn’t recognized. Turns out, I’d picked the wrong AMI (not Amazon Linux 2). After a quick &lt;code&gt;cat /etc/os-release&lt;/code&gt; check, I relaunched with the right AMI and the install worked like a charm.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenge 2: Port Problems
&lt;/h2&gt;

&lt;p&gt;I couldn’t load &lt;code&gt;http://&amp;lt;public-ip&amp;gt;&lt;/code&gt;. The culprit? My inbound rules in security group had port 443 (HTTPS) open instead of port 80 (HTTP). I had to remove port 443 &amp;amp; add inbound rules for port 80, then refreshed the page &amp;amp; Nginx was running, the welcome page popped up perfectly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8zrx7eabcn6o39yavvxa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8zrx7eabcn6o39yavvxa.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AMI Matters:&lt;/strong&gt; Picking the right image saves headaches with package managers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ports Are Key:&lt;/strong&gt; Security groups need to match your app’s needs—port 80 for HTTP, not 443 unless you’re ready for HTTPS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistence Pays:&lt;/strong&gt; Troubleshooting is half the fun of DevOps.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;What’s your favorite beginner AWS tip?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>cloud</category>
      <category>linux</category>
      <category>aws</category>
    </item>
  </channel>
</rss>
