<?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: Sarvar Nadaf</title>
    <description>The latest articles on DEV Community by Sarvar Nadaf (@sarvar_04).</description>
    <link>https://dev.to/sarvar_04</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%2F1163149%2F5afa2902-591e-4944-b6fa-9bbba80c6e95.png</url>
      <title>DEV Community: Sarvar Nadaf</title>
      <link>https://dev.to/sarvar_04</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sarvar_04"/>
    <language>en</language>
    <item>
      <title>Introducing AWS FinOps Agent - An AI That Investigates Your Cloud Costs</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Thu, 11 Jun 2026 19:10:23 +0000</pubDate>
      <link>https://dev.to/aws-builders/introducing-aws-finops-agent-an-ai-that-investigates-your-cloud-costs-2j3i</link>
      <guid>https://dev.to/aws-builders/introducing-aws-finops-agent-an-ai-that-investigates-your-cloud-costs-2j3i</guid>
      <description>&lt;p&gt;👋 Hey there, Tech Enthusiasts!&lt;/p&gt;

&lt;p&gt;I'm Sarvar, a Cloud Architect who loves turning complex tech problems into simple solutions. I've worked with AWS, Azure, DevOps, Data, Analytics, Generative-AI and Agentic-AI building real systems for real companies. In this article series, I'll share what I've learned in a way that's easy to follow, whether you're experienced or just getting started.&lt;/p&gt;

&lt;p&gt;Let's get into it! 🚀&lt;/p&gt;




&lt;p&gt;If you have managed AWS costs across multiple accounts, you know the pain. You get an alert at 9 AM that something spiked overnight. You open Cost Explorer, filter by service, then by account, then by region. Then it is over to CloudTrail to figure out what changed. By the time you trace it to a developer who launched a new instance type in a test account, two hours have passed. And that was just one anomaly.&lt;/p&gt;

&lt;p&gt;I have been doing this for years across organizations running 50 to 500 AWS accounts. Monthly cost reviews, chasing engineers for explanations, building reports in spreadsheets that nobody reads until budget meetings. When AWS announced the FinOps Agent in public preview on June 9, 2026, I cleared my afternoon and set it up.&lt;/p&gt;

&lt;p&gt;This article is my honest take after spending real time with it what it does well, where it fits in an enterprise setup, and how it changes the daily work for architects and FinOps teams.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is AWS FinOps Agent, in Plain Terms?
&lt;/h2&gt;

&lt;p&gt;It is an AI agent that sits on top of your existing AWS cost tools Cost Explorer, Cost Anomaly Detection, Cost Optimization Hub, Compute Optimizer, and CloudTrail and does the manual work you have been doing yourself.&lt;/p&gt;

&lt;p&gt;You ask it questions in plain English. It pulls data from those services, correlates events, and gives you answers. You can also tell it to run tasks on a schedule or react when a cost anomaly fires.&lt;/p&gt;

&lt;p&gt;It runs in us-east-1 during the preview, but it can see cost data from all your regions (except GovCloud and China). It is free during the preview period with a monthly usage cap.&lt;/p&gt;

&lt;p&gt;The underlying engine uses Amazon Bedrock foundation models, but you do not need to know or care about that. You just talk to it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Setting It Up Genuinely Simple
&lt;/h2&gt;

&lt;p&gt;I was expecting a 30-minute setup with IAM policy headaches. It took about 5 minutes.&lt;/p&gt;

&lt;p&gt;Here is the actual flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the AWS Console, switch to us-east-1, find FinOps Agent&lt;/li&gt;
&lt;li&gt;Click "Get Started" and give your agent a name&lt;/li&gt;
&lt;li&gt;It creates an IAM role for you with one click read-only access to billing and cost management services&lt;/li&gt;
&lt;li&gt;Optionally connect Jira and Slack&lt;/li&gt;
&lt;li&gt;Open the web application and start asking questions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is it. No CloudFormation stack, no Terraform module, no custom Lambda functions. The whole process felt like it was designed for people who have better things to do than fight with IAM policies.&lt;/p&gt;

&lt;p&gt;One thing to note: if you have multi-session enabled in your AWS Console, disable it first. I hit a permissions error during Slack setup that traced back to this. The documentation does not make this obvious upfront.&lt;/p&gt;

&lt;p&gt;Also, if you are setting up Slack, you need to add the FinOps Agent app as a member of the channel before configuring the integration in the console. Miss that step and you will get an error with no helpful message. The AWS docs mark it as "Important" but it is easy to skip over.&lt;/p&gt;




&lt;h2&gt;
  
  
  The First Question I Asked
&lt;/h2&gt;

&lt;p&gt;I opened the chat and typed:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What were my top 5 cost drivers last month, grouped by service and account?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Within about 15 seconds, it came back with a table showing exactly that. It pulled from Cost Explorer, broke it down by service, showed the account names, and included the percentage change from the previous month.&lt;/p&gt;

&lt;p&gt;No dashboard to build. No saved report to configure. Just a question and an answer.&lt;/p&gt;

&lt;p&gt;I then asked a follow-up about why EC2 costs went up in one of my accounts. The agent went deeper it identified the instance type that caused the increase, surfaced the relevant CloudTrail event showing when those instances were launched, and pointed to the IAM role that made the API call.&lt;/p&gt;

&lt;p&gt;That kind of investigation jumping between Cost Explorer and CloudTrail, matching timestamps, finding the right principal usually takes me 20 to 30 minutes. The agent did it in under a minute.&lt;/p&gt;

&lt;p&gt;One thing I learned quickly: by default, costs shown include credit adjustments. If you want pre-credit numbers (which is what most enterprises care about for budgeting), you need to say that in your prompt. I told the agent to always exclude credits, and it remembered that preference in my next session without me repeating it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where This Solves Real Enterprise Pain
&lt;/h2&gt;

&lt;p&gt;Let me map this to the problems I have seen in every organization I have worked with.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem 1: Cost Anomalies Go Uninvestigated
&lt;/h3&gt;

&lt;p&gt;Every enterprise I know has AWS Cost Anomaly Detection turned on. Most of them also have a shared email inbox or Slack channel where those alerts land. And in most cases, those alerts sit there for hours or days before someone looks at them.&lt;/p&gt;

&lt;p&gt;The reason is simple investigation is manual and time-consuming. AWS FinOps Agent automates this. You set up an automation that says: "When a cost anomaly is detected above $500, investigate it, find the root cause, and post the findings to our #finops-alerts Slack channel."&lt;/p&gt;

&lt;p&gt;From that point, every anomaly gets investigated the moment it arrives. The output includes what changed, the CloudTrail event that caused it, the IAM principal responsible, and a summary of the likely root cause.&lt;/p&gt;

&lt;p&gt;For a small FinOps team supporting many accounts, this is the difference between catching problems in hours versus catching them in days.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem 2: Engineers Cannot Self-Serve Cost Information
&lt;/h3&gt;

&lt;p&gt;In most enterprises, if a developer wants to understand their team's cost, they either need Cost Explorer access (which many organizations restrict) or they file a request with the central FinOps team.&lt;/p&gt;

&lt;p&gt;This creates a bottleneck. In my experience, the FinOps team ends up spending a significant chunk of their time answering basic questions like "How much did our staging environment cost last month?" or "Which of our Lambda functions is the most expensive?"&lt;/p&gt;

&lt;p&gt;With FinOps Agent, engineers ask their question, the agent answers it using real data, and the FinOps team never gets involved.&lt;/p&gt;

&lt;p&gt;You can also upload context files a CSV that maps accounts to team owners, a document that explains your tagging conventions, a list of which cost centers map to which business units. The agent uses this context to understand questions like "What is Team Alpha spending?" without anyone having to explain which accounts belong to Team Alpha.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem 3: Monthly Reports Are a Time Sink
&lt;/h3&gt;

&lt;p&gt;Every FinOps team I have worked with produces some version of a monthly cost report. It goes to finance, to engineering leadership, sometimes to the CTO. Building it involves exporting data from Cost Explorer, formatting it in a slide deck or spreadsheet, adding commentary about what changed, and sending it out.&lt;/p&gt;

&lt;p&gt;This takes 4 to 8 hours per month in my experience. Some teams spend even longer because different stakeholders want different views.&lt;/p&gt;

&lt;p&gt;FinOps Agent lets you schedule these. You tell it: "Every Monday at 8 AM, generate a cost report for last week broken down by business unit, highlight any changes over 10%, and deliver it as a PDF to the #finops-weekly Slack channel."&lt;/p&gt;

&lt;p&gt;It runs on schedule. No human involvement. The report shows up in Slack every Monday. You can set up different reports for different audiences a detailed one for engineering leads, a summary for finance, an executive view for the CTO.&lt;/p&gt;

&lt;p&gt;The output formats are HTML, PDF, or PPT all presentation-ready.&lt;/p&gt;

&lt;h3&gt;
  
  
  Problem 4: Optimization Recommendations Sit in a Dashboard Nobody Checks
&lt;/h3&gt;

&lt;p&gt;AWS Cost Optimization Hub and Compute Optimizer both produce solid recommendations rightsizing opportunities, idle resources, Savings Plans suggestions. The problem is that these recommendations live in AWS dashboards that engineers rarely check.&lt;/p&gt;

&lt;p&gt;FinOps Agent can pull these recommendations, summarize them, and create Jira tickets assigned to the team that owns each resource. Instead of a FinOps analyst manually reviewing the dashboard and creating tickets, the agent does it on a schedule.&lt;/p&gt;

&lt;p&gt;Example automation: "Every Thursday, check Cost Optimization Hub for new recommendations over $100/month in potential savings. For each one, create a Jira ticket in the INFRA project with the recommendation details, the affected resource, and the estimated savings."&lt;/p&gt;

&lt;p&gt;Now optimization work flows into engineering sprints naturally, without someone manually copying data between tools.&lt;/p&gt;




&lt;h2&gt;
  
  
  How It Fits Into an Enterprise Architecture
&lt;/h2&gt;

&lt;p&gt;Let me describe how I would deploy this in a typical multi-account enterprise setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Management Account Setup
&lt;/h3&gt;

&lt;p&gt;You create the FinOps Agent in your management account (or the account that has the consolidated billing payer role). This gives it visibility across all member accounts. The IAM role it creates is read-only by default it cannot make changes to your infrastructure.&lt;/p&gt;

&lt;p&gt;Member account owners can also create their own agent scoped to just their account if they want independent cost visibility without management account access. This means you can have multiple agents with different scopes a central one for the FinOps team and individual ones for teams that want their own view without seeing the full organization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Access Patterns
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;How They Use It&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;FinOps Team&lt;/td&gt;
&lt;td&gt;Set up automations for anomaly investigation, scheduled reports, optimization ticket creation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Engineering Leads&lt;/td&gt;
&lt;td&gt;Ask ad-hoc cost questions about their team's spend&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Finance&lt;/td&gt;
&lt;td&gt;Receive scheduled reports in their preferred format&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Platform Team&lt;/td&gt;
&lt;td&gt;Monitor shared infrastructure costs, investigate spikes in shared services&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Individual Engineers&lt;/td&gt;
&lt;td&gt;Self-serve answers about their service's cost impact&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  How the Pieces Connect
&lt;/h3&gt;

&lt;p&gt;The agent reads from your existing AWS cost services Cost Explorer for spend data, Cost Anomaly Detection for alerts, Cost Optimization Hub and Compute Optimizer for recommendations, and CloudTrail for change history. It writes only to Jira and Slack when you configure those integrations and trigger a task. It does not modify any AWS resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security Model
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;IAM role-based access (you control what it can read)&lt;/li&gt;
&lt;li&gt;Read-only by default against billing services&lt;/li&gt;
&lt;li&gt;Write actions (Jira tickets, Slack posts) only happen when you configure and trigger them&lt;/li&gt;
&lt;li&gt;All activity logged in CloudTrail for audit&lt;/li&gt;
&lt;li&gt;No cross-account write permissions&lt;/li&gt;
&lt;li&gt;The agent uses Amazon Bedrock foundation models to process your queries your cost data is accessed through IAM roles within your own account and does not leave AWS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One thing worth noting for architects evaluating risk: this service has zero infrastructure dependency. Your actual cost tools, accounts, and resources are unchanged. If the agent were deprecated tomorrow, you would be back to manual work not rebuilding systems. That makes it a low-risk addition to your environment during preview.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Noticed During Hands-On Testing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Good
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Speed of answers.&lt;/strong&gt; Asking a cost question and getting a real answer in 15-30 seconds changes your workflow. I found myself asking follow-up questions I would never have bothered investigating manually.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Root cause correlation.&lt;/strong&gt; The fact that it connects Cost Anomaly Detection alerts to CloudTrail events automatically is genuinely useful. This is the step that takes the most time in manual investigation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scheduled tasks actually work.&lt;/strong&gt; I set up a weekly report for Thursday morning. It fired within 2 minutes of the scheduled time, and the PDF it produced was clean and readable. Results also get stored in an "Artifacts" tab in the web app for later access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context files are powerful.&lt;/strong&gt; After uploading an account-to-team mapping, the agent started answering team-level questions correctly without any additional configuration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory across sessions.&lt;/strong&gt; I told it to always exclude credits when showing cost totals. In my next session, it remembered that preference without me repeating it.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Rough Edges (It Is Preview After All)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Slack messages are links, not inline content.&lt;/strong&gt; When the agent posts to Slack, it sends a link back to the FinOps Agent web app rather than the full analysis inline. This means you still need to click through to see the details. For a quick glance during a busy morning, this adds friction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sending to Slack is not automatic.&lt;/strong&gt; After the agent completes an analysis in a chat session, you still need to explicitly prompt "Please send this to Slack." Event-triggered automations handle routing on their own, but for ad-hoc queries you need that extra step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;English only.&lt;/strong&gt; Prompts in other languages sometimes work, but responses are always in English. Some prompts get a flat "Only English is supported" response. Global enterprises will need multi-language support at GA.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Single region availability.&lt;/strong&gt; The agent only runs in us-east-1 during preview. It can see multi-region cost data, but the console experience requires you to be in that region.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No IaC support yet.&lt;/strong&gt; You cannot create the agent via CloudFormation or Terraform. Console-only setup for now. For enterprises that mandate infrastructure as code for everything, this is a blocker until GA.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Compute Optimizer queries are region-scoped.&lt;/strong&gt; When I asked for rightsizing recommendations, it only checked us-east-1 by default. You need to specify other regions explicitly in your prompt. Easy to miss if you have workloads spread across multiple regions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Trust but verify.&lt;/strong&gt; Like any AI system, the agent can occasionally misidentify a root cause or produce an incomplete analysis. In the early days, treat its output as a strong starting point that still needs a human eye before you act on it or forward it to engineering. Once you have seen enough accurate results to build confidence, you can reduce oversight.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real Workflow: Before and After
&lt;/h2&gt;

&lt;p&gt;Let me lay out what a typical week looks like for a FinOps team before and after adopting this. These numbers assume automations are tuned and the agent is producing reliable output getting there takes a week or two of initial validation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before FinOps Agent
&lt;/h3&gt;

&lt;p&gt;Monday morning:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check Cost Anomaly Detection alerts from the weekend (5 alerts) - 10 minutes&lt;/li&gt;
&lt;li&gt;Open Cost Explorer for each alert, filter by account/service/time - 15 minutes per alert&lt;/li&gt;
&lt;li&gt;Open CloudTrail, search for relevant API calls - 10 minutes per alert&lt;/li&gt;
&lt;li&gt;Write up findings and post to Slack - 5 minutes per alert&lt;/li&gt;
&lt;li&gt;Total: about 2.5 hours for 5 anomalies&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Wednesday:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pull Cost Optimization Hub, review new recommendations - 30 minutes&lt;/li&gt;
&lt;li&gt;Create Jira tickets for engineering teams - 45 minutes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Friday:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Build weekly cost report for leadership - 1.5 hours&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Weekly total for routine FinOps work: roughly 5-6 hours&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  After FinOps Agent (once automations are stable)
&lt;/h3&gt;

&lt;p&gt;Monday morning:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check Slack - 5 anomaly investigations already posted over the weekend with root causes identified&lt;/li&gt;
&lt;li&gt;Review findings, confirm no false positives - 15 minutes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Wednesday:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check Jira - optimization tickets already created for engineering teams&lt;/li&gt;
&lt;li&gt;Review for accuracy - 10 minutes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Friday:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Weekly report already delivered to Slack on Thursday evening&lt;/li&gt;
&lt;li&gt;Quick review for anything unusual - 5 minutes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Weekly total: roughly 30 minutes of review instead of 5-6 hours of manual work&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That freed-up time goes to strategic work: Savings Plans analysis, architecture reviews for cost efficiency, building chargeback models, negotiating Enterprise Discount Programs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who Should Try This Today
&lt;/h2&gt;

&lt;p&gt;If you are in one of these situations, set it up this week:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;FinOps team of 1-3 people supporting many accounts.&lt;/strong&gt; The automation alone justifies the setup time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Engineering organization where developers do not have Cost Explorer access.&lt;/strong&gt; Give them the agent instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Any team that produces recurring cost reports manually.&lt;/strong&gt; Automate them immediately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Organizations where cost anomaly alerts go uninvestigated.&lt;/strong&gt; Event-triggered investigation fixes this.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are a solo developer with one account and minimal spend, this is probably more than you need right now. But if your monthly bill is complex enough that you spend time understanding it, it is worth trying.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A word of caution:&lt;/strong&gt; This is a preview service. There is no SLA, features may change before GA, and you should not wire it into critical production workflows without a plan for what happens if the service changes or pricing is introduced. Use it to learn and validate, and be ready to adjust when GA arrives.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Early Customers Are Reporting
&lt;/h2&gt;

&lt;p&gt;Several companies shared their experience during the preview period. Here is what stood out to me:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workday&lt;/strong&gt; runs their AI platform across many AWS accounts. Their team was spending hours every month chasing cost outliers and building leadership reports. They said that anomaly detection and reporting now happens from one natural-language interface, replacing what used to be hours of manual dashboard work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Convera&lt;/strong&gt;, a global commercial payments company in a regulated environment, focused on catching small cost changes before they compound. They described the agent completing the full loop detecting anomalies, investigating root cause, and creating Jira tickets for the right engineer so issues reach the owner directly instead of sitting in a shared queue.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mitre 10&lt;/strong&gt;, a New Zealand retailer with a lean platform team, was splitting time between reliability work and cost governance. They now define investigation workflows once and have them run continuously, instead of relying on someone remembering to check.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AVIV Group&lt;/strong&gt; operates digital marketplaces across France, Germany, and Belgium with hundreds of AWS accounts. As they shift to a hybrid FinOps model, the agent answers engineer questions directly, freeing the central team for strategy and leadership reporting.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Am Watching For at GA
&lt;/h2&gt;

&lt;p&gt;A few things I want to see before this moves from "useful preview" to "enterprise standard":&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Pricing at GA.&lt;/strong&gt; It is free during preview. Enterprise adoption depends on what it costs relative to the time it saves.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IaC support.&lt;/strong&gt; We need CloudFormation and Terraform support to deploy this in governed environments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inline Slack responses.&lt;/strong&gt; The current link-based approach adds friction. Full findings posted inline would make the Slack integration much more natural.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-language support.&lt;/strong&gt; Global enterprises need this.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IAM Identity Center integration.&lt;/strong&gt; For SSO-based access control in organizations that have moved away from IAM users.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deeper Slack interaction.&lt;/strong&gt; Currently delivery-only engineers cannot ask the agent questions from within Slack. That would complete the loop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-region Compute Optimizer queries by default.&lt;/strong&gt; Should not require specifying regions manually.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Bottom Line
&lt;/h2&gt;

&lt;p&gt;AWS FinOps Agent does not replace your FinOps team. It replaces the repetitive, manual investigation work that consumes most of their time. It takes the data that already exists across Cost Explorer, CloudTrail, Cost Optimization Hub, and Compute Optimizer, and makes it accessible through conversation instead of dashboards.&lt;/p&gt;

&lt;p&gt;For architects, it means getting cost answers during design reviews without waiting for someone to run a report. For FinOps teams, it means spending time on strategy instead of triage. For engineering teams, it means getting cost context delivered to them in Jira and Slack instead of learning yet another AWS console.&lt;/p&gt;

&lt;p&gt;It took me 5 minutes to set up, and within the first hour I had it answering questions, investigating anomalies, and scheduling weekly reports. For a preview service, that is a strong start.&lt;/p&gt;

&lt;p&gt;The setup guide is at: &lt;a href="https://docs.aws.amazon.com/finops-agent/latest/userguide/what-is.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/finops-agent/latest/userguide/what-is.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The console is at: &lt;a href="https://us-east-1.console.aws.amazon.com/finops-agent/home" rel="noopener noreferrer"&gt;https://us-east-1.console.aws.amazon.com/finops-agent/home&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try it while it is free. Worst case, you spend 10 minutes setting it up and decide it is not for you yet. Best case, you automate away hours of weekly grunt work and spend that time on work that actually needs a human brain.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written after hands-on testing during the public preview period (June 2026). All features and behaviors described reflect the preview state and may change at general availability.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📌 Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Thanks for reading! If this was helpful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❤️ Like if it added value&lt;/li&gt;
&lt;li&gt;💾 Save for later&lt;/li&gt;
&lt;li&gt;🔄 Share with your team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Follow me for more on:&lt;/strong&gt; AWS architecture, FinOps, DevOps, and AI Infrastructure.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;Visit my website&lt;/a&gt;&lt;/strong&gt; | &lt;strong&gt;&lt;a href="https://www.linkedin.com/in/sarvar04/" rel="noopener noreferrer"&gt;Connect on LinkedIn&lt;/a&gt;&lt;/strong&gt; | &lt;strong&gt;Email:&lt;/strong&gt; &lt;a href="mailto:simplynadaf@gmail.com"&gt;simplynadaf@gmail.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Happy Learning&lt;/strong&gt; 🚀&lt;/p&gt;

</description>
      <category>aws</category>
      <category>ai</category>
      <category>agents</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Can AI Agents Browse Login-Protected Dashboards? I Tested It</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Tue, 09 Jun 2026 11:35:23 +0000</pubDate>
      <link>https://dev.to/aws-builders/i-gave-my-ai-agent-a-real-browser-heres-what-actually-happened-4ipk</link>
      <guid>https://dev.to/aws-builders/i-gave-my-ai-agent-a-real-browser-heres-what-actually-happened-4ipk</guid>
      <description>&lt;p&gt;👋 Hey there, Tech Enthusiasts!&lt;/p&gt;

&lt;p&gt;I'm Sarvar, a Cloud Architect who loves turning complex tech problems into simple solutions. I've worked with AWS, Azure, DevOps, Data, Analytics, Generative-AI and Agentic-AI building real systems for real companies. In this article series, I'll share what I've learned in a way that's easy to follow, whether you're experienced or just getting started.&lt;/p&gt;

&lt;p&gt;Let's get into it! 🚀&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Your agent can write Terraform, deploy infrastructure, and debug pipelines. But ask it to check a dashboard behind Cloudflare? It's useless."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;If you read my previous article &lt;a href="https://dev.to/aws-builders/i-let-an-ai-agent-become-my-devops-engineer-529"&gt;I Let an AI Agent Become My DevOps Engineer&lt;/a&gt; you know I've been pushing AI agents into real operational workflows. Code generation, CI/CD pipelines, infrastructure provisioning agents handle all of that now.&lt;/p&gt;

&lt;p&gt;But there's one area where every agent I've used just... stops working. The browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here's the reality:&lt;/strong&gt; Most of the tools I monitor daily Grafana dashboards, AWS Console, CI pipelines, internal wikis live behind login walls, CAPTCHAs, and anti-bot protection. My agents can't touch them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's where &lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct&lt;/a&gt; comes in.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I found this tool that claims to give AI agents real browser control not headless puppeteer scripts that get blocked instantly, but actual anti-detection browsing with CAPTCHA handling and human handoff built in.&lt;/p&gt;

&lt;p&gt;I spent a week testing it. Here's what I found.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;By the end of this article, you'll:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand why AI agents fail at real browser tasks&lt;/li&gt;
&lt;li&gt;Install &lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct&lt;/a&gt; and run your first extraction&lt;/li&gt;
&lt;li&gt;See how anti-detection browsing actually works&lt;/li&gt;
&lt;li&gt;Test human handoff for 2FA/login scenarios&lt;/li&gt;
&lt;li&gt;Run parallel browser sessions without cross-contamination&lt;/li&gt;
&lt;li&gt;Turn a repeated workflow into a reusable Skill&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Time Required:&lt;/strong&gt; 30 minutes (15 min read + 15 min hands-on)&lt;br&gt;
&lt;strong&gt;Difficulty:&lt;/strong&gt; Intermediate&lt;br&gt;
&lt;strong&gt;Prerequisites:&lt;/strong&gt; Python 3.12+, Node.js 18+, Google Chrome, terminal access&lt;/p&gt;


&lt;h2&gt;
  
  
  The Problem: Agents Can't Browse the Real Web
&lt;/h2&gt;
&lt;h3&gt;
  
  
  The Reality of AI Agent Automation in 2026
&lt;/h3&gt;

&lt;p&gt;I manage infrastructure across multiple AWS accounts. Every morning I check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CloudWatch dashboards&lt;/li&gt;
&lt;li&gt;GitHub notifications and PR reviews&lt;/li&gt;
&lt;li&gt;CI/CD pipeline status&lt;/li&gt;
&lt;li&gt;Internal monitoring tools&lt;/li&gt;
&lt;li&gt;Competitor product pages (for research)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's easily 30-40 minutes of tab-switching, scrolling, and context-gathering before I even start real work.&lt;/p&gt;

&lt;p&gt;I thought my agent can write Terraform and deploy entire VPCs. Surely it can open a webpage and read some data?&lt;/p&gt;

&lt;p&gt;Nope.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Attempt 1: Basic web fetch&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://monitoring-dashboard.internal.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;403 Forbidden - Access Denied
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Attempt 2: Headless Puppeteer&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://dashboard.example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Blocked by Cloudflare challenge&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Page stuck on verification challenge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Attempt 3: Selenium&lt;/strong&gt;&lt;br&gt;
Same story. Detected as bot within 2 seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problems:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anti-bot systems detect headless browsers instantly&lt;/li&gt;
&lt;li&gt;CAPTCHAs stop automation cold&lt;/li&gt;
&lt;li&gt;2FA/login flows need a human but there's no way to hand off&lt;/li&gt;
&lt;li&gt;Running multiple accounts in one browser gets all of them flagged&lt;/li&gt;
&lt;li&gt;Every script breaks when the site changes layout&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;My daily routine before &lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct&lt;/a&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open 6 tabs manually&lt;/li&gt;
&lt;li&gt;Log into GitHub, AWS Console, Grafana MFA for each&lt;/li&gt;
&lt;li&gt;Scroll through notifications, check pipeline status&lt;/li&gt;
&lt;li&gt;Copy-paste data into Slack for the team&lt;/li&gt;
&lt;li&gt;Repeat next morning&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;40 minutes. Every. Day.&lt;/p&gt;

&lt;p&gt;Sound familiar? I needed something built specifically for this a browser that agents can actually use on the real web.&lt;/p&gt;


&lt;h2&gt;
  
  
  What is &lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct&lt;/a&gt;?
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Simple Version
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct&lt;/a&gt; is a CLI that gives your AI agent a real Chrome browser with anti-detection, CAPTCHA solving, and human handoff built in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Think of it like this:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Puppeteer/Playwright&lt;/strong&gt; = giving your agent a browser that screams "I'M A BOT"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct&lt;/a&gt;&lt;/strong&gt; = giving your agent a browser that looks and behaves like a real person&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  The Five Things It Actually Does
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Gets past anti-bot walls:&lt;/strong&gt; Real fingerprints, proxy rotation, stealth browsing. Sites don't know it's automated.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Handles CAPTCHAs automatically:&lt;/strong&gt; Cloudflare, DataDome, reCAPTCHA. Solves what it can, escalates what it can't.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hands off to humans when stuck:&lt;/strong&gt; Hit a QR code login? SMS verification? It generates a URL. You (or a teammate) open it on your phone, do the human step, and the agent continues from where it stopped.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Runs parallel tasks without interference:&lt;/strong&gt; Three sessions checking three different things under the same account. They don't step on each other.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Turns workflows into reusable Skills:&lt;/strong&gt; Did something once? Package it. Run it again without the agent having to figure it out from scratch.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before starting, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.12+ (&lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct &lt;/a&gt;CLI is Python-based)&lt;/li&gt;
&lt;li&gt;Node.js 18+ (for &lt;code&gt;npx skills&lt;/code&gt; Skill installation)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;uv&lt;/code&gt; package manager (or &lt;code&gt;pip&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Google Chrome installed&lt;/li&gt;
&lt;li&gt;Terminal/CLI access&lt;/li&gt;
&lt;li&gt;An AI agent that can run shell commands (Claude Code, Cursor, Kiro, Codex or just run commands yourself)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Quick Check
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# Python 3.12+&lt;/span&gt;

node &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# v18+&lt;/span&gt;

google-chrome &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# Google Chrome 149.x.x.x&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Faqjihrw33bn2z7qgyqh5.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%2Faqjihrw33bn2z7qgyqh5.png" alt=" " width="736" height="211"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you don't have &lt;code&gt;uv&lt;/code&gt; (fast Python package manager):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-LsSf&lt;/span&gt; https://astral.sh/uv/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Flxw7szahj173py29zqoa.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%2Flxw7szahj173py29zqoa.png" alt=" " width="800" height="144"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you don't have Chrome:&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="c"&gt;# Ubuntu/Debian&lt;/span&gt;
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; ./google-chrome-stable_current_amd64.deb

&lt;span class="c"&gt;# Amazon Linux&lt;/span&gt;
wget https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm
&lt;span class="nb"&gt;sudo &lt;/span&gt;yum localinstall &lt;span class="nt"&gt;-y&lt;/span&gt; google-chrome-stable_current_x86_64.rpm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F1bwb4rs2eh4kczkcxeiy.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%2F1bwb4rs2eh4kczkcxeiy.png" alt=" " width="799" height="224"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What We're Building
&lt;/h2&gt;

&lt;p&gt;Here's what we're going to test today:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────┐
│                 AI Agent (You/Claude/Cursor)    │
└─────────────────────┬───────────────────────────┘
                      │ CLI commands
                      ▼
┌─────────────────────────────────────────────────┐
│              BrowserAct CLI                     │
│  ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │
│  │ Stealth  │ │ Sessions │ │  Human Handoff   │ │
│  │ Browser  │ │ Manager  │ │  (remote-assist) │ │
│  └──────────┘ └──────────┘ └──────────────────┘ │
└─────────────────────┬───────────────────────────┘
                      │ Real Chrome
                      ▼
┌─────────────────────────────────────────────────┐
│  Protected Websites (Cloudflare, Login Walls)   │
└─────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 1: Install &lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Two parts here the Skill definition (for AI agents) and the actual CLI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install the Skill (for agent integration):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add browser-act/skills &lt;span class="nt"&gt;--skill&lt;/span&gt; browser-act &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F0ttzxwdc5wvq71pj8pwm.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%2F0ttzxwdc5wvq71pj8pwm.png" alt=" " width="800" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;◇  Installation complete

│  ✓ ~/.agents/skills/browser-act
│    universal: Amp, Antigravity, Antigravity CLI, Cline, Codex +12 more
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Install the CLI itself:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv tool &lt;span class="nb"&gt;install &lt;/span&gt;browser-act-cli &lt;span class="nt"&gt;--python&lt;/span&gt; 3.12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F3ililpofsj7wtf9i7amy.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%2F3ililpofsj7wtf9i7amy.png" alt=" " width="800" height="342"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Resolved 90 packages in 437ms
Installed 90 packages in 2.09s
Installed 1 executable: browser-act
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Verify it works (required on first install):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act get-skills core &lt;span class="nt"&gt;--skill-version&lt;/span&gt; 2.0.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns the environment state, available browsers, and command reference. Run this before anything else it completes the version handshake.&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%2Fxvcp1ssgkn4m6ok180r3.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%2Fxvcp1ssgkn4m6ok180r3.png" alt=" " width="800" height="169"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Get Your &lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct &lt;/a&gt;API Key
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Go to &lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct&lt;/a&gt; and sign in to your account.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on your profile email address in the top-right corner.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;From the dropdown menu, select &lt;strong&gt;API Keys&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click &lt;strong&gt;Manage Keys&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select &lt;strong&gt;Create Key&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enter a name for your API key (for example, &lt;code&gt;Amazon-Q&lt;/code&gt;, &lt;code&gt;MCP-Server&lt;/code&gt;, or &lt;code&gt;Development&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Copy the generated API key and store it securely. You may not be able to view the full key again after leaving the page.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&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%2Ff05z3wluzzpbgr2z7xdg.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%2Ff05z3wluzzpbgr2z7xdg.png" alt=" " width="800" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Treat your API key like a password. Do not share it publicly or commit it to source code repositories.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set your API key&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act auth &lt;span class="nb"&gt;set&lt;/span&gt; &amp;lt;your-api-key&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F7bgow0rn50pbc7cc4gq4.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%2F7bgow0rn50pbc7cc4gq4.png" alt=" " width="796" height="80"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;API key saved.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 2: Your First Extraction (Zero Config)
&lt;/h2&gt;

&lt;p&gt;The simplest thing &lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct&lt;/a&gt; can do extract content from a page without any setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act stealth-extract https://httpbin.org/ip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fmllan8126qts9qv894sh.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%2Fmllan8126qts9qv894sh.png" alt=" " width="800" height="155"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Output from my test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"origin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"100.54.212.44"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean, rendered content. No HTML tags, no noise. Just the data.&lt;/p&gt;

&lt;p&gt;Let's try something with actual content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act stealth-extract https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Faaxzyblr372608sjr3fl.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%2Faaxzyblr372608sjr3fl.png" alt=" " width="798" height="110"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Example Domain&lt;/span&gt;
This domain is for use in documentation examples without needing permission. Avoid use in operations.
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;Learn more&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;https://iana.org/domains/example&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Already in markdown format. My agent can read this directly without any parsing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What &lt;code&gt;stealth-extract&lt;/code&gt; does under the hood:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spins up a lightweight stealth browser&lt;/li&gt;
&lt;li&gt;Visits the URL with anti-detection fingerprint&lt;/li&gt;
&lt;li&gt;Renders JavaScript (unlike curl)&lt;/li&gt;
&lt;li&gt;Returns content in markdown&lt;/li&gt;
&lt;li&gt;Tears down the browser&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Important note from testing:&lt;/strong&gt; &lt;code&gt;stealth-extract&lt;/code&gt; works great for quick reads on most sites. For heavily protected sites (like nowsecure.nl with aggressive Cloudflare), you'll need a full browser session (Step 3) it has stronger anti-detection because it maintains a persistent fingerprint and proxy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Full Browser Control
&lt;/h2&gt;

&lt;p&gt;Now let's do something more interesting interactive browser automation. First, you need a stealth browser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act browser create &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"test-stealth"&lt;/span&gt; &lt;span class="nt"&gt;--type&lt;/span&gt; stealth &lt;span class="nt"&gt;--desc&lt;/span&gt; &lt;span class="s2"&gt;"Testing for article"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F0vlnrq8msg6wpt23trh6.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%2F0vlnrq8msg6wpt23trh6.png" alt=" " width="795" height="76"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;99703194156616493 name="test-stealth" type=stealth&lt;/span&gt;
  &lt;span class="py"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Testing for article"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now open a session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act &lt;span class="nt"&gt;--session&lt;/span&gt; my-research browser open 99703194156616493 https://github.com/trending
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fisi23ocl53hwbohvqaph.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%2Fisi23ocl53hwbohvqaph.png" alt=" " width="799" height="74"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;session_name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;my-research&lt;/span&gt;
&lt;span class="py"&gt;browser_type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;stealth&lt;/span&gt;
&lt;span class="py"&gt;url&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;https://github.com/trending&lt;/span&gt;
&lt;span class="py"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;github.com/trending&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;See what's on the page (indexed elements):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act &lt;span class="nt"&gt;--session&lt;/span&gt; my-research state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fizavir6p4ckb4awfveqb.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%2Fizavir6p4ckb4awfveqb.png" alt=" " width="799" height="214"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Real output from my test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;url=https://github.com/trending
title=Trending repositories on GitHub today · GitHub

|SCROLL|&amp;lt;html class=js-focus-visible /&amp;gt; (0.0 pages above, 1.2 pages below)

  [1]&amp;lt;a aria-label=Homepage /&amp;gt;
  [3]&amp;lt;button type=button aria-expanded=false /&amp;gt;
      Platform
  [4]&amp;lt;button type=button aria-expanded=false /&amp;gt;
      Solutions
  [8]&amp;lt;a class=NavLink-module__link__EG3d4 /&amp;gt;
      Pricing
  [9]&amp;lt;qbsearch-input class=search-input /&amp;gt;
      [10]&amp;lt;div class=search-input-container search-with-dialog /&amp;gt;
  [12]&amp;lt;a /&amp;gt;
      Sign in
  [15]&amp;lt;a class=js-selected-navigation-item /&amp;gt;
      Explore
  [17]&amp;lt;a class=js-selected-navigation-item selected /&amp;gt;
      Trending
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See those numbers? That's how the agent interacts. No DOM parsing, no CSS selectors. Just:&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="c"&gt;# Click the search input (element 10)&lt;/span&gt;
browser-act &lt;span class="nt"&gt;--session&lt;/span&gt; my-research click 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fkpksstby31qiqlye2o4k.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%2Fkpksstby31qiqlye2o4k.png" alt=" " width="800" height="64"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;clicked&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The page updates search box opens. Now type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act &lt;span class="nt"&gt;--session&lt;/span&gt; my-research input 10 &lt;span class="s2"&gt;"browser automation AI"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fhttwtswt8qawbxi8lp5b.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%2Fhttwtswt8qawbxi8lp5b.png" alt=" " width="794" height="49"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;input&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"browser automation AI"&lt;/span&gt; &lt;span class="s"&gt;element=10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;This is what they mean by "designed for agent reasoning."&lt;/strong&gt; The output is compact, indexed, and token-efficient. My agent doesn't waste tokens parsing HTML.&lt;/p&gt;

&lt;p&gt;You can also grab the full page as markdown:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act &lt;span class="nt"&gt;--session&lt;/span&gt; my-research get markdown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fzn38qfhuc67cdm5eephl.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%2Fzn38qfhuc67cdm5eephl.png" alt=" " width="799" height="271"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Returns the entire page content in clean markdown format. Useful when you want to extract data rather than interact.&lt;/p&gt;

&lt;p&gt;When you're done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act session close my-research
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fc3n9lotoka0oq5eil7mm.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%2Fc3n9lotoka0oq5eil7mm.png" alt=" " width="800" height="73"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;session_name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;my-research closed=true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step 4: Anti-Bot in Action
&lt;/h2&gt;

&lt;p&gt;Here's where it gets real. I pointed &lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct&lt;/a&gt; at &lt;a href="https://nowsecure.nl" rel="noopener noreferrer"&gt;nowsecure.nl&lt;/a&gt; a site specifically designed to test anti-bot detection. It runs Cloudflare challenges.&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="c"&gt;# Full browser session on a Cloudflare-protected site&lt;/span&gt;
browser-act &lt;span class="nt"&gt;--session&lt;/span&gt; antibot browser open 99703194156616493 https://nowsecure.nl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F6qyme4vwia38pnlz2c2i.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%2F6qyme4vwia38pnlz2c2i.png" alt=" " width="796" height="82"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;session_name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;antibot&lt;/span&gt;
&lt;span class="py"&gt;browser_type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;stealth&lt;/span&gt;
&lt;span class="py"&gt;url&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;https://nowsecure.nl/&lt;/span&gt;
&lt;span class="py"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;nowsecure.nl&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It got through. Let me verify by pulling the content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act &lt;span class="nt"&gt;--session&lt;/span&gt; antibot get markdown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fc0wfkqm2n15yndmb7een.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%2Fc0wfkqm2n15yndmb7een.png" alt=" " width="799" height="187"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nowsecure.nl
NOWSECURE
---------
### by nodriver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And checking the network traffic shows exactly what happened Cloudflare's turnstile challenge was handled automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act &lt;span class="nt"&gt;--session&lt;/span&gt; antibot network requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fh8axz4tawr4llz9nzxiz.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%2Fh8axz4tawr4llz9nzxiz.png" alt=" " width="800" height="207"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# format: csv
...
GET,200,Script,application/javascript,...,https://challenges.cloudflare.com/turnstile/v0/g/8fc8ed1d8752/api.js
GET,200,Document,text/html,...,https://challenges.cloudflare.com/cdn-cgi/challenge-platform/h/g/turnstile/...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The stealth browser handled the Cloudflare verification without any manual intervention. No CAPTCHA prompt, no block.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct&lt;/a&gt; uses three layers to get through:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Environment layer&lt;/strong&gt; - Stealth fingerprint, TLS config, proxy switching. Most blocks never trigger.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Execution layer&lt;/strong&gt; - If a CAPTCHA appears, &lt;code&gt;solve-captcha&lt;/code&gt; handles it automatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human layer&lt;/strong&gt; - If auto-solve fails, it can hand off to you (more on this next).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I'm not going to claim it works on every site. It doesn't. No tool does. But for the monitoring dashboards and research pages I tested, it got through where Puppeteer and Selenium couldn't.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Human Handoff (This Is the Killer Feature)
&lt;/h2&gt;

&lt;p&gt;This is the part that sold me. Here's the scenario:&lt;/p&gt;

&lt;p&gt;Your agent is automating a workflow. It hits a login page that requires SMS verification or QR code scan. In Puppeteer world, the automation just dies. Game over.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct&lt;/a&gt; does something different:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act &lt;span class="nt"&gt;--session&lt;/span&gt; login-task remote-assist &lt;span class="nt"&gt;--objective&lt;/span&gt; &lt;span class="s2"&gt;"complete 2FA verification"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fkfg46qg6x5yb5az6zd0z.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%2Fkfg46qg6x5yb5az6zd0z.png" alt=" " width="800" height="116"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Real output from my test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Remote assist session created.

Share this URL with the user:
  https://www.browseract.com/remote-cli/d83544c39e4a4e6ba1cc98f95050e615
expires in 1h 0m

Human assist is now active - the browser is under user control.
Do not send browser commands until the user finishes the assist session.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You open that URL on your phone or another device. You see the browser the actual live browser state. You complete the SMS verification, scan the QR code, whatever. Then you close it.&lt;/p&gt;

&lt;p&gt;The agent gets notified and continues from the exact same browser state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this matters for DevOps:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Internal tools with SSO that requires periodic re-auth&lt;/li&gt;
&lt;li&gt;AWS Console with MFA&lt;/li&gt;
&lt;li&gt;Third-party dashboards with 2FA&lt;/li&gt;
&lt;li&gt;Any workflow where "fully automated" isn't realistic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent doesn't crash. It doesn't restart. It waits, you help, it continues. That's practical automation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Parallel Sessions (Multi-Task Without Conflicts)
&lt;/h2&gt;

&lt;p&gt;Here's a real use case from my work: I want an agent to check three things simultaneously under the same account.&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="c"&gt;# Session 1: Check GitHub trending&lt;/span&gt;
browser-act &lt;span class="nt"&gt;--session&lt;/span&gt; check-trending browser open 99703194156616493 https://github.com/trending
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Feg2jz7d6zl90hr6dzg02.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%2Feg2jz7d6zl90hr6dzg02.png" alt=" " width="800" height="72"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;session_name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;check-trending&lt;/span&gt;
&lt;span class="py"&gt;browser_type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;stealth&lt;/span&gt;
&lt;span class="py"&gt;url&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;https://github.com/trending&lt;/span&gt;
&lt;span class="py"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;github.com/trending&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Session 2: Check GitHub topics (same browser, parallel session)&lt;/span&gt;
browser-act &lt;span class="nt"&gt;--session&lt;/span&gt; check-topics browser open 99703194156616493 https://github.com/topics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fbqoil1hcav9grkfslh0a.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%2Fbqoil1hcav9grkfslh0a.png" alt=" " width="797" height="78"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;session_name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;check-topics&lt;/span&gt;
&lt;span class="py"&gt;browser_type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;stealth&lt;/span&gt;
&lt;span class="py"&gt;url&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;https://github.com/topics&lt;/span&gt;
&lt;span class="py"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;github.com/topics&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two sessions. One browser. They don't interfere with each other.&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="c"&gt;# See all active sessions&lt;/span&gt;
browser-act session list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Frbezsr58mt43evgomy00.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%2Frbezsr58mt43evgomy00.png" alt=" " width="800" height="472"&gt;&lt;/a&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="na"&gt;session_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;check-trending&lt;/span&gt;
&lt;span class="na"&gt;browser_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stealth&lt;/span&gt;
&lt;span class="na"&gt;browser_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;99703194156616493&lt;/span&gt;
&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Trending repositories on GitHub today · GitHub&lt;/span&gt;
&lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/trending&lt;/span&gt;

&lt;span class="na"&gt;session_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;check-topics&lt;/span&gt;
&lt;span class="na"&gt;browser_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stealth&lt;/span&gt;
&lt;span class="na"&gt;browser_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;99703194156616493&lt;/span&gt;
&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Topics on GitHub · GitHub&lt;/span&gt;
&lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/topics&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each session has its own navigation state but shares the login cookies. So you log in once, and all tasks can work in parallel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The three concurrency models:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;What's shared&lt;/th&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Cross-browser parallel&lt;/td&gt;
&lt;td&gt;Nothing (independent identity)&lt;/td&gt;
&lt;td&gt;Multi-account monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Same-browser multi-session&lt;/td&gt;
&lt;td&gt;Login state&lt;/td&gt;
&lt;td&gt;Parallel tasks, one account&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Privacy mode&lt;/td&gt;
&lt;td&gt;Nothing (fresh each time)&lt;/td&gt;
&lt;td&gt;One-off scraping, anonymity&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When done:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act session close check-trending
browser-act session close check-topics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;session_name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;check-trending closed=true&lt;/span&gt;
&lt;span class="py"&gt;session_name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;check-topics closed=true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sessions auto-reclaim after 8 hours if you forget. But clean up after yourself.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Skill Forge Make It Reusable
&lt;/h2&gt;

&lt;p&gt;Let's say Step 6 worked great. I want to run that "check my GitHub morning routine" every day without the agent figuring it out from scratch each time.&lt;/p&gt;

&lt;p&gt;First, install Skill Forge:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add browser-act/skills &lt;span class="nt"&gt;--skill&lt;/span&gt; browser-act-skill-forge &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fbznezwtyppiziksx2vz8.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%2Fbznezwtyppiziksx2vz8.png" alt=" " width="799" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;◇  Installation complete

│  ✓ ~/.agents/skills/browser-act-skill-forge
│    universal: Amp, Antigravity, Antigravity CLI, Cline, Codex +12 more
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then tell your agent:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Forge a Skill that checks GitHub notifications, extracts unread count and top 5 notification titles."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Skill Forge will:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Explore the page structure&lt;/li&gt;
&lt;li&gt;Discover the best extraction path&lt;/li&gt;
&lt;li&gt;Generate a reusable Skill file&lt;/li&gt;
&lt;li&gt;Test it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Next time, the agent just runs the Skill. No re-exploration, no token waste. Same stable path every time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where I'd use this:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Daily dashboard checks&lt;/li&gt;
&lt;li&gt;Competitor price monitoring&lt;/li&gt;
&lt;li&gt;Pull request summary generation&lt;/li&gt;
&lt;li&gt;CI/CD status aggregation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Where I wouldn't:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sites that change layout constantly&lt;/li&gt;
&lt;li&gt;One-off tasks that'll never repeat&lt;/li&gt;
&lt;li&gt;Anything that needs real-time interaction (chat, live support)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Bonus: How I'd Use This in Production
&lt;/h2&gt;

&lt;p&gt;I ran a few extra tests to see how &lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct&lt;/a&gt; fits into a real cloud operations workflow. Here's what I found.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitoring AWS Health Dashboard
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act &lt;span class="nt"&gt;--session&lt;/span&gt; aws-health browser open 99703194156616493 https://status.aws.amazon.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F7obglp5sl5apgowyoosg.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%2F7obglp5sl5apgowyoosg.png" alt=" " width="798" height="78"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;session_name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;aws-health&lt;/span&gt;
&lt;span class="py"&gt;browser_type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;stealth&lt;/span&gt;
&lt;span class="py"&gt;url&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;https://health.aws.amazon.com/health/status&lt;/span&gt;
&lt;span class="py"&gt;title&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;health.aws.amazon.com/health/status&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then extract the status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act &lt;span class="nt"&gt;--session&lt;/span&gt; aws-health get markdown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F0mw1y66m7ejch6yr0o7a.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%2F0mw1y66m7ejch6yr0o7a.png" alt=" " width="799" height="202"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS Health Dashboard
====================
Service health - Jun 09, 2026
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Take a screenshot for your Slack channel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act &lt;span class="nt"&gt;--session&lt;/span&gt; aws-health screenshot /tmp/aws-health.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fwq55up7o324eqdluan0q.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%2Fwq55up7o324eqdluan0q.png" alt=" " width="793" height="50"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;saved: /tmp/aws-health.png
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Real screenshot, 315KB, exactly what the dashboard looks like. Ship that to a Slack webhook and your team gets a visual status check every morning.&lt;/p&gt;




&lt;h3&gt;
  
  
  Checking GitHub Status (CI Monitoring)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act &lt;span class="nt"&gt;--session&lt;/span&gt; aws-health navigate https://www.githubstatus.com
browser-act &lt;span class="nt"&gt;--session&lt;/span&gt; aws-health get markdown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F60v02b7zxwjhwvlsu29o.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%2F60v02b7zxwjhwvlsu29o.png" alt=" " width="800" height="252"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;All Systems Operational
-----------------------
Git Operations   99.85% uptime  Normal
Webhooks         99.96% uptime  Normal
API Requests     99.99% uptime  Normal
Issues           99.97% uptime  Normal
Pull Requests    99.61% uptime  Normal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now my agent has real uptime data it can act on. If Pull Requests drops below 99%, alert me.&lt;/p&gt;




&lt;h3&gt;
  
  
  Browsing Cloudflare's Own Product Page (Their Anti-Bot)
&lt;/h3&gt;

&lt;p&gt;The ultimate test can &lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct&lt;/a&gt; get through Cloudflare's own website?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act &lt;span class="nt"&gt;--session&lt;/span&gt; aws-health navigate https://www.cloudflare.com/products/
browser-act &lt;span class="nt"&gt;--session&lt;/span&gt; aws-health get markdown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Ftuxjqzrl0jwu53fdljw2.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%2Ftuxjqzrl0jwu53fdljw2.png" alt=" " width="799" height="216"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Cloudflare Products
-------------------
Everything you need to build, deploy, and scale applications...
Workers Global serverless functions
D1 - Serverless SQL database
R2 - Object storage...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes. It extracted their full product catalog through their own protection. That's something.&lt;/p&gt;




&lt;h3&gt;
  
  
  JavaScript Evaluation (Custom Data Extraction)
&lt;/h3&gt;

&lt;p&gt;Need something specific that the markdown extraction doesn't give you? Run JavaScript directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act &lt;span class="nt"&gt;--session&lt;/span&gt; aws-health &lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s2"&gt;"document.title"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Ftcdp7h3sk4jcbhctwbsb.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%2Ftcdp7h3sk4jcbhctwbsb.png" alt=" " width="795" height="75"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Products | Cloudflare
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act &lt;span class="nt"&gt;--session&lt;/span&gt; aws-health &lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s2"&gt;"document.querySelectorAll('a').length"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fsgy9xs5cpn0nm2slyk4z.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%2Fsgy9xs5cpn0nm2slyk4z.png" alt=" " width="800" height="30"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;122
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets you write precise extraction logic without relying on the markdown parser.&lt;/p&gt;




&lt;h3&gt;
  
  
  My Production Architecture (What I'd Actually Build)
&lt;/h3&gt;

&lt;p&gt;Here's the setup I'm planning for my AWS monitoring workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌────────────────────────────────────────────────────────────────┐
│                    Cron Job (Every Morning 8 AM)                │
└──────────────────────────┬─────────────────────────────────────┘
                           │
                           ▼
┌────────────────────────────────────────────────────────────────┐
│                   AI Agent (Kiro / Claude Code)                 │
│                                                                │
│  1. browser-act --session grafana browser open &amp;lt;id&amp;gt; &amp;lt;url&amp;gt;      │
│  2. browser-act --session grafana get markdown                 │
│  3. browser-act --session grafana screenshot ./grafana.png     │
│  4. browser-act --session cloudwatch browser open &amp;lt;id&amp;gt; &amp;lt;url&amp;gt;   │
│  5. browser-act --session cloudwatch get markdown              │
│  6. Parse data → Generate summary                             │
│  7. Post to Slack with screenshots                            │
│  8. Close all sessions                                         │
└──────────────────────────┬─────────────────────────────────────┘
                           │
                           ▼
┌────────────────────────────────────────────────────────────────┐
│                 Slack Channel: #morning-status                  │
│                                                                │
│  Morning Infra Report - Jun 04, 2026                           │
│  All AWS services operational                                  │
│  GitHub: 99.85% uptime                                         │
│  Grafana: CPU alert on prod-api-3                              │
│  [dashboard-screenshot.png]                                    │
└────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why this works better than API-only monitoring:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some dashboards don't expose APIs (internal Grafana, vendor portals)&lt;/li&gt;
&lt;li&gt;Screenshots give visual context that JSON data can't&lt;/li&gt;
&lt;li&gt;Human handoff means SSO re-auth doesn't break the whole pipeline&lt;/li&gt;
&lt;li&gt;One browser session stays logged in no re-auth every day&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Production considerations:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run on a dedicated EC2 instance (t3.medium is enough tested)&lt;/li&gt;
&lt;li&gt;Use static proxy for stable identity (avoids triggering "new device" alerts)&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;--desc&lt;/code&gt; on browsers with site/account info so future sessions know what's what&lt;/li&gt;
&lt;li&gt;Monitor credit usage each session costs credits, budget accordingly&lt;/li&gt;
&lt;li&gt;Store screenshots in S3, link in Slack messages&lt;/li&gt;
&lt;li&gt;If login expires, &lt;code&gt;remote-assist&lt;/code&gt; lets you re-auth from your phone without SSH-ing in&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Before vs After: How This Changed My Morning
&lt;/h2&gt;

&lt;p&gt;Let me be real about what my daily routine looked like before and after testing &lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Before &lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct&lt;/a&gt; (My Old Morning)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;8:00 AM - Open laptop
8:02 AM - Open GitHub, hit MFA prompt on phone, wait
8:04 AM - Check notifications (12 unread), scan PRs (3 need review)
8:08 AM - Open AWS Console, MFA again, navigate to CloudWatch
8:12 AM - Check 3 dashboards across 2 accounts, screenshot for team
8:18 AM - Open Grafana, login, scroll through panels looking for alerts
8:23 AM - Check GitHub Actions 2 repos have failing builds
8:27 AM - Open competitor's changelog page (Cloudflare protected)
8:30 AM - Copy-paste a summary into Slack for the team
8:35 AM - Realize I missed one account, go back to AWS...
8:40 AM - Actually start working
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;40 minutes of monkey work. Every morning. On good days.&lt;/p&gt;

&lt;p&gt;On bad days a session expires mid-check, or MFA doesn't arrive, or Cloudflare blocks my automation script. Then it's 50+ minutes.&lt;/p&gt;

&lt;h3&gt;
  
  
  After &lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct&lt;/a&gt; (What I'm Building Now)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;8:00 AM - Agent runs automatically (cron trigger)

Agent:
  → Opens stealth session on GitHub
  → Extracts: 12 notifications, 3 PRs pending review
  → Navigates to GitHub Actions: 2 failing builds (api-server, docs-deploy)
  → Opens parallel session: AWS Health Dashboard
  → Extracts: "All Systems Operational"
  → Screenshots CloudWatch dashboard → saves to S3
  → Navigates to competitor changelog: extracts new features list
  → Posts summary to Slack #morning-status

8:01 AM - Slack notification pops up with the full morning report.

8:02 AM - I read the summary with my coffee. Actually start working.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Time saved: ~38 minutes/day. That's 3+ hours/week.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And the thing is it's not just the time. It's the mental context switching. Opening six different login-protected dashboards pulls you out of focus. Having a one-page summary waiting in Slack means I start my day knowing exactly what needs attention.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Honest Part
&lt;/h3&gt;

&lt;p&gt;It's not fully there yet. Here's my current reality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;What works today:&lt;/strong&gt; The extraction, screenshots, and parallel sessions all work as shown in this article. I tested every command above on a real EC2 instance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What I'm still setting up:&lt;/strong&gt; The cron automation + Slack integration pipeline. That's a week of wiring. I'll write a follow-up when it's running.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The one catch:&lt;/strong&gt; First login to each site needs me to &lt;code&gt;remote-assist&lt;/code&gt; from my phone (handle MFA manually). After that, the session stays authenticated for days. So it's "almost" fully automated I handle MFA once a week, the agent handles the other 4 days.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Where I Wouldn't Use This
&lt;/h3&gt;

&lt;p&gt;Being honest not everything needs browser automation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AWS resource monitoring&lt;/strong&gt; - Use CloudWatch alarms + SNS. APIs exist. Don't browser-scrape what you can API-call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simple uptime checks&lt;/strong&gt; - Use UptimeRobot or similar. Don't overkill it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data that changes every second&lt;/strong&gt; - &lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct&lt;/a&gt; isn't real-time. It's periodic checks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Banking or highly sensitive portals&lt;/strong&gt; - Too risky. Keep that manual.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Where It Shines
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dashboards without APIs&lt;/strong&gt; - Grafana (free tier), internal tools, vendor portals&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-account visual checks&lt;/strong&gt; - AWS Console across 3 accounts, screenshot each&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Competitor monitoring&lt;/strong&gt; - Product pages behind Cloudflare, pricing pages that block scrapers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CI/CD status aggregation&lt;/strong&gt; - Pull data from GitHub Actions, CircleCI, Jenkins (web UI) into one summary&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Any "check and report" workflow&lt;/strong&gt; that you do manually more than twice a week&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What Worked and What Didn't
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Worked Well
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stealth browser sessions&lt;/strong&gt; - get through Cloudflare where Puppeteer/Selenium can't. Tested on nowsecure.nl and cloudflare.com itself both passed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real dashboard extraction&lt;/strong&gt; - pulled structured data from AWS Health Dashboard, GitHub Status, and Cloudflare product pages. Real production use cases, all worked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Screenshots&lt;/strong&gt; - &lt;code&gt;screenshot&lt;/code&gt; command saves PNGs directly. 315KB for a full-page capture. Great for visual monitoring and Slack reports.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Indexed interaction&lt;/strong&gt; - the state/click/input model is clean. Way better than parsing DOM. Real element indices, real clicks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human handoff&lt;/strong&gt; - generates a live URL instantly. This solves a real problem I've had for years.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session isolation&lt;/strong&gt; - ran two parallel sessions on the same browser, completely independent. No conflicts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Navigation within sessions&lt;/strong&gt; - &lt;code&gt;navigate&lt;/code&gt; command lets you move across sites within one session. Open AWS Health, then navigate to GitHub Status, same session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript eval&lt;/strong&gt; - &lt;code&gt;eval&lt;/code&gt; lets you run custom extraction when markdown isn't precise enough.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network capture&lt;/strong&gt; - &lt;code&gt;network requests&lt;/code&gt; shows exactly what's happening under the hood. Great for debugging.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Install was clean&lt;/strong&gt; - &lt;code&gt;uv tool install&lt;/code&gt; pulled 90 packages, had it running in under a minute on a t3.medium.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Mixed
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;stealth-extract&lt;/code&gt; vs full sessions&lt;/strong&gt; - &lt;code&gt;stealth-extract&lt;/code&gt; works for quick reads on normal sites, but for heavily protected sites you need a full stealth browser session. Not obvious from the docs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CAPTCHA solving&lt;/strong&gt; - &lt;code&gt;solve-captcha&lt;/code&gt; returned "no compatible captcha found" when I tested (page had already loaded past it). Couldn't trigger a scenario where it was needed during my testing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed&lt;/strong&gt; - stealth browsing is slower than raw Puppeteer. Takes a few seconds to open sessions. Makes sense (it's doing more work) but worth noting.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Could Be Better
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Documentation&lt;/strong&gt; - the &lt;code&gt;get-skills&lt;/code&gt; command dumps a massive guide. Useful but overwhelming on first read.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error messages&lt;/strong&gt; - "Browser launch failed: Connection closed" doesn't tell you much. Took trial and error to figure out I needed a full session for protected sites.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Requires API key for stealth&lt;/strong&gt; - can't use the anti-detection features without signing up first. Fair, but the free tier is limited.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Pricing &amp;amp; Credits
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct&lt;/a&gt; runs on a credit system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free credits:&lt;/strong&gt; 100 credits on signup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paid plans:&lt;/strong&gt; Check &lt;a href="https://www.browseract.com/pricing" rel="noopener noreferrer"&gt;browseract.com/pricing&lt;/a&gt; for current rates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For testing and writing this article, the free credits were enough. For production monitoring workflows running daily, you'd need a paid plan.&lt;/p&gt;




&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Issue 1: "browser-act: command not found"
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash: browser-act: &lt;span class="nb"&gt;command &lt;/span&gt;not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Install via uv (not npm):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-LsSf&lt;/span&gt; https://astral.sh/uv/install.sh | sh
uv tool &lt;span class="nb"&gt;install &lt;/span&gt;browser-act-cli &lt;span class="nt"&gt;--python&lt;/span&gt; 3.12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The binary installs to &lt;code&gt;~/.local/bin/&lt;/code&gt; make sure it's in your PATH.&lt;/p&gt;




&lt;h3&gt;
  
  
  Issue 2: "Browser launch failed: Connection closed"
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Browser launch failed: Browser.close: Connection closed while reading from the driver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; This happens when &lt;code&gt;stealth-extract&lt;/code&gt; can't handle a heavily protected site. Use a full browser session instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act browser create &lt;span class="nt"&gt;--name&lt;/span&gt; &lt;span class="s2"&gt;"my-stealth"&lt;/span&gt; &lt;span class="nt"&gt;--type&lt;/span&gt; stealth &lt;span class="nt"&gt;--desc&lt;/span&gt; &lt;span class="s2"&gt;"browsing"&lt;/span&gt;
browser-act &lt;span class="nt"&gt;--session&lt;/span&gt; task1 browser open &amp;lt;browser-id&amp;gt; &amp;lt;url&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Issue 3: "api_key: not configured"
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CLI:
  api_key: not configured
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; You need an API key for stealth browser features:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act auth &lt;span class="nb"&gt;set&lt;/span&gt; &amp;lt;your-api-key&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get one at &lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;browseract.com&lt;/a&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Issue 4: Chrome not found
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;Error&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Chrome executable not found&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Install Chrome:&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="c"&gt;# Ubuntu&lt;/span&gt;
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; ./google-chrome-stable_current_amd64.deb

&lt;span class="c"&gt;# Amazon Linux&lt;/span&gt;
wget https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm
&lt;span class="nb"&gt;sudo &lt;/span&gt;yum localinstall &lt;span class="nt"&gt;-y&lt;/span&gt; google-chrome-stable_current_x86_64.rpm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Issue 5: Session already exists
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Session name 'my-task' already in use
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Close it first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act session close my-task
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Real-World Use Cases
&lt;/h2&gt;

&lt;p&gt;As a cloud architect, here's where I see this fitting:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Infrastructure Monitoring&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check multiple dashboards (Grafana, CloudWatch, Datadog) through a single agent&lt;/li&gt;
&lt;li&gt;Generate daily summaries from web-based monitoring tools&lt;/li&gt;
&lt;li&gt;Alert on visual changes in dashboards that don't have API access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. DevOps Workflows&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automate PR review summaries across repos&lt;/li&gt;
&lt;li&gt;Check CI/CD status across multiple platforms&lt;/li&gt;
&lt;li&gt;Monitor deployment pipelines with login-protected UIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Multi-Account Operations&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manage multiple AWS accounts through Console (when CLI isn't enough)&lt;/li&gt;
&lt;li&gt;Monitor multiple SaaS dashboards&lt;/li&gt;
&lt;li&gt;Cross-account compliance checks on web portals&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Research &amp;amp; Data Collection&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Track competitor features and pricing pages&lt;/li&gt;
&lt;li&gt;Aggregate release notes from multiple vendor sites&lt;/li&gt;
&lt;li&gt;Collect public data from protected listing pages&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Key Concepts Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Sessions = Task Workspaces
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browser-act &lt;span class="nt"&gt;--session&lt;/span&gt; &amp;lt;name&amp;gt; &amp;lt;&lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every task gets its own session. Sessions don't interfere. Name them descriptively.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Browsers = Identities
&lt;/h3&gt;

&lt;p&gt;Different browsers = different fingerprints, proxies, cookies. Use separate browsers for separate accounts. Three types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;chrome&lt;/strong&gt; - imports your local Chrome login state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;chrome-direct&lt;/strong&gt; - controls your running Chrome directly&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;stealth&lt;/strong&gt; - anti-detection browser with fingerprint masking (needs API key)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Skills = Reusable Workflows
&lt;/h3&gt;

&lt;p&gt;Once something works, package it as a Skill. Next time it runs without re-exploration.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Three-Layer Anti-Blocking
&lt;/h3&gt;

&lt;p&gt;Environment (fingerprint) → Execution (auto-solve) → Human (handoff). Progressive escalation.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Two Extraction Modes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;stealth-extract&lt;/code&gt;&lt;/strong&gt; - quick, zero-config, good for simple reads. Lightweight.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full browser session&lt;/strong&gt; - persistent, stronger anti-detection, needed for heavily protected sites.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;I'm building the full pipeline I described above. Here's my roadmap:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Week 1 (done):&lt;/strong&gt; Test &lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct&lt;/a&gt; on real sites this article&lt;br&gt;
&lt;strong&gt;Week 2:&lt;/strong&gt; Wire up the morning monitoring agent (cron + BrowserAct + Slack webhook)&lt;br&gt;
&lt;strong&gt;Week 3:&lt;/strong&gt; Add Skill Forge package the workflow so it's stable across page layout changes&lt;br&gt;
&lt;strong&gt;Week 4:&lt;/strong&gt; Run for 30 days, track: time saved, credits consumed, failures, manual interventions&lt;/p&gt;

&lt;p&gt;If there's interest, I'll write a follow-up with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Actual production usage data (cost per month, reliability %)&lt;/li&gt;
&lt;li&gt;The Skill file I built for GitHub monitoring&lt;/li&gt;
&lt;li&gt;How many times &lt;code&gt;remote-assist&lt;/code&gt; saved me from a broken pipeline&lt;/li&gt;
&lt;li&gt;Token usage comparison vs doing the same thing with raw Playwright&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is simple: I want my mornings back. 40 minutes of tab-switching replaced by a 2-minute Slack read. &lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct&lt;/a&gt; is the missing piece between "my agent can code" and "my agent can actually see what's happening in production."&lt;/p&gt;




&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;BrowserAct:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;Website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/browser-act/skills/tree/main/browser-act" rel="noopener noreferrer"&gt;GitHub browser-act Skill&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/browser-act/skills/tree/main/browser-act-skill-forge" rel="noopener noreferrer"&gt;GitHub Skill Forge&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/browser-act/skills/tree/main/docs" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/browser-act/skills/blob/main/docs/concurrency.md" rel="noopener noreferrer"&gt;Concurrency &amp;amp; Isolation Docs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;My Previous Article:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/aws-builders/i-let-an-ai-agent-become-my-devops-engineer-529"&gt;I Let an AI Agent Become My DevOps Engineer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Here's what I found after testing &lt;a href="https://browseract.com?fpr=sarvar04" rel="noopener noreferrer"&gt;BrowserAct&lt;/a&gt; on an EC2 instance (Amazon Linux, t3.medium):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stealth browser sessions get through Cloudflare tested on nowsecure.nl and cloudflare.com itself&lt;/li&gt;
&lt;li&gt;Extracted real data from AWS Health Dashboard, GitHub Status  actual production monitoring working&lt;/li&gt;
&lt;li&gt;Screenshots save as PNG ready to pipe to Slack/S3 for visual dashboards&lt;/li&gt;
&lt;li&gt;Human handoff generates a live remote URL in seconds actually works&lt;/li&gt;
&lt;li&gt;Parallel sessions run independently on the same browser no conflicts&lt;/li&gt;
&lt;li&gt;Navigation + eval let you build complex multi-step extraction workflows&lt;/li&gt;
&lt;li&gt;Indexed interaction (state, click, input) is agent-friendly and token-efficient&lt;/li&gt;
&lt;li&gt;Skill Forge makes repeated workflows reusable&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;stealth-extract&lt;/code&gt; has limits use full sessions for protected sites&lt;/li&gt;
&lt;li&gt;Needs API key for anti-detection features&lt;/li&gt;
&lt;li&gt;Error messages could be clearer when things fail&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Bottom line:&lt;/strong&gt; If you're running AI agents and they need to interact with real websites not just APIs this is worth testing. I'm already planning a daily monitoring pipeline with it. It's not magic. It won't bypass everything. But it solves real problems that I haven't seen other tools handle this cleanly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Connect &amp;amp; Share
&lt;/h2&gt;

&lt;p&gt;If this was useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Star the &lt;a href="https://github.com/browser-act/skills" rel="noopener noreferrer"&gt;BrowserAct GitHub repo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Drop a comment what workflows would you automate?&lt;/li&gt;
&lt;li&gt;Share with your team&lt;/li&gt;
&lt;li&gt;Follow me for the follow-up article&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📌 Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Thanks for reading! If this was helpful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❤️ Like if it added value&lt;/li&gt;
&lt;li&gt;💾 Save for later&lt;/li&gt;
&lt;li&gt;🔄 Share with your team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Follow me for more on:&lt;/strong&gt; AWS architecture, FinOps, DevOps, and AI Infrastructure.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;Visit my website&lt;/a&gt;&lt;/strong&gt; | &lt;strong&gt;&lt;a href="https://www.linkedin.com/in/sarvar04/" rel="noopener noreferrer"&gt;Connect on LinkedIn&lt;/a&gt;&lt;/strong&gt; | &lt;strong&gt;Email:&lt;/strong&gt; &lt;a href="mailto:simplynadaf@gmail.com"&gt;simplynadaf@gmail.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Happy Learning&lt;/strong&gt; 🚀&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>ai</category>
      <category>automation</category>
      <category>agents</category>
    </item>
    <item>
      <title>I Built an AI Agent That Tailors My Resume - Here's How Agents Actually Work</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Mon, 25 May 2026 13:32:31 +0000</pubDate>
      <link>https://dev.to/aws-builders/i-built-an-ai-agent-that-tailors-my-resume-heres-how-agents-actually-work-5733</link>
      <guid>https://dev.to/aws-builders/i-built-an-ai-agent-that-tailors-my-resume-heres-how-agents-actually-work-5733</guid>
      <description>&lt;p&gt;👋 Hey there, tech enthusiasts!&lt;/p&gt;

&lt;p&gt;I'm Sarvar, a Cloud Architect who loves turning complex tech problems into simple solutions. I've worked with AWS, Azure, DevOps, Data, Analytics, Generative-AI and Agentic-AI building real systems for real companies. In this article series, I'll share what I've learned in a way that's easy to follow, whether you're experienced or just getting started.&lt;/p&gt;

&lt;p&gt;Let's get into it! 🚀&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article is for beginners who've heard about AI agents but haven't built one yet.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem That Started It All
&lt;/h2&gt;

&lt;p&gt;You're looking for a job. You open LinkedIn, Naukri, Indeed search "Senior Python Developer." Hundreds of results pop up. Every company wants something slightly different. One wants AWS + Django. Another wants FastAPI + Kubernetes. The third one wants Flask + microservices.&lt;/p&gt;

&lt;p&gt;You have all these skills. That's not the problem.&lt;/p&gt;

&lt;p&gt;The problem is time. To stand out, you need to customize your resume for each role. Read the job description carefully. Figure out what they care about. Rewrite your summary. Reorder skills. Tweak bullet points. Write a cover letter. Hit submit.&lt;/p&gt;

&lt;p&gt;That's ONE application. Now do that 10 more times.&lt;/p&gt;

&lt;p&gt;It's not a skill problem. It's a time problem. You're doing the same thing over and over read, match, rewrite, submit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What if you could build something that handles all of this for you?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You give it your resume and say "Find Senior Python roles in Bangalore, 20-35 LPA (Lakhs Per Annum ₹20-35 lakh salary), remote-friendly" and it goes to work. It reads job descriptions, matches your skills, finds gaps, rewrites your resume for each role, writes cover letters, and ranks everything by how well you fit.&lt;/p&gt;

&lt;p&gt;Same you. Same resume. Same job market. But the boring repetitive part? Done automatically.&lt;/p&gt;

&lt;p&gt;That's what got me into agentic AI. Not the hype. The simple thought: "I can automate this annoying thing."&lt;/p&gt;




&lt;h2&gt;
  
  
  ChatGPT vs AI Agents What's the Difference?
&lt;/h2&gt;

&lt;p&gt;People ask me this all the time. "Isn't ChatGPT already an agent?"&lt;/p&gt;

&lt;p&gt;Not really. Here's the simple difference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ChatGPT&lt;/strong&gt; - you ask a question, you get text back. Yes, it can browse the web and run code now. But YOU are still driving. You ask, it responds. You ask again, it responds again. It waits for you at every step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An AI Agent&lt;/strong&gt; - you give it a goal and it goes and does the work. Multiple steps. Multiple tools. Without you guiding each step. It decides what to do next on its own.&lt;/p&gt;

&lt;p&gt;Here's a simple comparison:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;You ask: "Find me Senior Python jobs in Bangalore"&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ChatGPT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;"Here are some tips: update your LinkedIn, use relevant keywords..." (gives advice, waits for your next question)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AI Agent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Actually searches job boards, finds 47 matches, filters to 12 relevant ones, tailors your resume for each (does the work start to finish)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;One waits for you. The other goes and does the work.&lt;/strong&gt; That's the whole difference.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Agents Think?
&lt;/h2&gt;

&lt;p&gt;Every agent follows the same basic pattern:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Think → Do → Check → Repeat&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's what that looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────┐     ┌─────────┐     ┌─────────┐
│  THINK  │───▶│   DO    │────▶│  CHECK  │
│ What's  │     │ Take    │     │ Did it  │
│ next?   │     │ action  │     │ work?   │
└─────────┘     └─────────┘     └─────┬───┘
     ▲                                │
     │          Not done yet          │
     └────────────────────────────────┘
              ✅ Done? → Stop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. The agent looks at the goal, figures out the next step, does it, checks if it worked, and keeps going until the job is done.&lt;/p&gt;

&lt;p&gt;(You might hear people call this "ReAct" which stands for Reason + Act. It's a formal way of saying the agent thinks about what to do, then does it, then thinks again. But at its core, it's just the loop above.)&lt;/p&gt;

&lt;p&gt;Let me show you how this works with the resume example:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; "Tailor this resume for an Amazon SDE role."&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Agent reads the job description. Amazon wants Python, AWS, and distributed systems.&lt;/li&gt;
&lt;li&gt;Agent reads your resume. Python and AWS are there. But distributed systems? It's buried in one bullet point that nobody would notice.&lt;/li&gt;
&lt;li&gt;Agent rewrites that bullet point to make distributed systems stand out.&lt;/li&gt;
&lt;li&gt;Agent checks again are all the important keywords from the job description covered? Not yet. Kafka is missing.&lt;/li&gt;
&lt;li&gt;Agent looks at your experience. You actually used Kafka at your last job but never put it on the resume. (We all do this.)&lt;/li&gt;
&lt;li&gt;Agent adds Kafka to the relevant section.&lt;/li&gt;
&lt;li&gt;Final check. Everything important is covered. Done.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Seven steps. No human needed. The agent figured out what was missing, fixed it, checked its work, and moved on.&lt;/p&gt;

&lt;p&gt;This is how every agent works whether it's tailoring resumes, monitoring servers, or analyzing data. Same loop, different tasks. (Okay, I'm oversimplifying a bit real agents sometimes have more complex decision trees. But this mental model will get you through 90% of what you'll build.)&lt;/p&gt;




&lt;h2&gt;
  
  
  The Autonomy Spectrum How Much Freedom Should an Agent Have?
&lt;/h2&gt;

&lt;p&gt;Before we look at how to build an agent, let's understand how much freedom you can give one. "Agent" isn't all-or-nothing. It's a range.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Level&lt;/th&gt;
&lt;th&gt;What Happens&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Risk&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fully Manual&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Human does everything&lt;/td&gt;
&lt;td&gt;You tailor your resume by hand&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Assisted&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AI suggests, human does&lt;/td&gt;
&lt;td&gt;ChatGPT suggests bullet points, you copy-paste&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Orchestrated&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Human defines steps, agent follows them&lt;/td&gt;
&lt;td&gt;You say "Step 1: read JD, Step 2: rewrite summary" agent does exactly that&lt;/td&gt;
&lt;td&gt;Low-Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Semi-Autonomous&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agent decides steps, pauses for approval&lt;/td&gt;
&lt;td&gt;Agent figures out what to do, asks before submitting&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fully Autonomous&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agent does everything, no human involved&lt;/td&gt;
&lt;td&gt;Agent searches, tailors, submits you just get a report&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Most real-world agents today are in the middle some human control, some agent freedom.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My advice:&lt;/strong&gt; Start with orchestrated you define the workflow, the agent executes it. Once you trust it, give it more freedom gradually. Add human checkpoints for important steps. Fully autonomous? Maybe someday. Not yet for anything that matters.&lt;/p&gt;

&lt;p&gt;The resume tailor agent we're building in this series? Semi-autonomous. It figures out what to fix and does the work, but shows you the result before anything gets submitted.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Building Blocks of Every Agent
&lt;/h2&gt;

&lt;p&gt;Now that you know how much freedom an agent can have, let's look at what you actually configure to make one work. Most frameworks I've used have these same pieces. Let's group them by what they do.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Core: Who does what?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;LLM (The Brain)&lt;/strong&gt; - The AI model that does the thinking. GPT-4, Claude, Amazon Nova, Llama. Don't always use the most expensive model. Use cheap models for easy tasks, powerful models for hard tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Role (The Identity)&lt;/strong&gt; - Tell the agent WHO it is. Example: "You are a Senior Tech Recruiter with 15 years of experience." The more specific the role, the better the output. Vague roles give vague results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Goal (The Finish Line)&lt;/strong&gt; - When should the agent stop?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Bad: "Help with job search"&lt;/li&gt;
&lt;li&gt;✅ Good: "Find 5 Senior Python roles in Bangalore, 20-35 LPA, remote-friendly, from companies older than 3 years"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Task (The Instruction)&lt;/strong&gt; - The specific work to do right now. "Read this job description. Compare it against the resume. List the top 5 skill gaps." Be as specific as possible the more you leave unclear, the more the agent makes up.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Context: What does the agent know?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Backstory (The Personality)&lt;/strong&gt; - Shapes HOW the agent approaches work. Same task, different backstory, different style:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Seasoned recruiter" → practical, beats resume-scanning software&lt;/li&gt;
&lt;li&gt;"Career coach" → encouraging, focused on growth&lt;/li&gt;
&lt;li&gt;"Brutally honest reviewer" → tears your resume apart (but you learn the most)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Knowledge Base (The Facts)&lt;/strong&gt; - Information you hand the agent upfront. "The candidate is a Senior Python Developer. Based in Bangalore. Prefers remote. Salary: 20-35 LPA." Without this, the agent guesses. And it guesses wrong.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory (What It Remembers)&lt;/strong&gt; - Two types:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Short-term:&lt;/strong&gt; Remembers what happened earlier in the current task. "I already found Python listed in the resume."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Long-term:&lt;/strong&gt; Remembers across sessions. Run it next week, it knows what worked last time.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  The Safety Net: What keeps it under control?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Tools (The Hands)&lt;/strong&gt; - What the agent can actually DO beyond generating text: search the web, read files, call APIs, run code. Without tools, it just writes. With tools, it takes action. Not every agent needs tools a resume rewriter just processes text, but a job searcher needs API access.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Guardrails (The Limits)&lt;/strong&gt; - Rules that keep the agent in check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Max attempts → stop after X tries (otherwise it loops forever and burns your budget)&lt;/li&gt;
&lt;li&gt;Allowed actions → limit which tools it can use&lt;/li&gt;
&lt;li&gt;Output checks → verify the result makes sense&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I always set guardrails. Always. I learned this at 2 AM watching my bill climb because an agent got stuck in a loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Human-in-the-Loop (The Pause Button)&lt;/strong&gt; - For important decisions, make the agent stop and ask. The agent tailored your resume and is about to submit it. Instead of auto-submitting, it shows you the final version. "Should I submit this?" You review, approve, it continues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Automate the boring parts. Keep human judgment for the important decisions.&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Quick Reference
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group&lt;/th&gt;
&lt;th&gt;Pieces&lt;/th&gt;
&lt;th&gt;One-line summary&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;The Core&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;LLM, Role, Goal, Task&lt;/td&gt;
&lt;td&gt;Who is this agent and what's it doing?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;The Context&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Backstory, Knowledge Base, Memory&lt;/td&gt;
&lt;td&gt;What does it know?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;The Safety Net&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Tools, Guardrails, Human-in-the-Loop&lt;/td&gt;
&lt;td&gt;What can it do and what stops it?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Ten pieces, three groups. Now let's see how multiple agents work together.&lt;/p&gt;




&lt;h2&gt;
  
  
  Multiple Agents Working Together
&lt;/h2&gt;

&lt;p&gt;One agent is useful. But what if the job is too big for one?&lt;/p&gt;

&lt;p&gt;You split the work. Just like a real team each person handles what they're best at.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Think of it like a restaurant:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One chef doesn't do everything. There's someone who preps ingredients, someone who cooks, and someone who plates the dish.&lt;/li&gt;
&lt;li&gt;Each person does one thing well. Together, they serve a full meal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Agents work the same way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Our resume example with 3 agents:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Agent&lt;/th&gt;
&lt;th&gt;Job&lt;/th&gt;
&lt;th&gt;Output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Job Analyzer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reads the job description, pulls out what matters most&lt;/td&gt;
&lt;td&gt;"They want Python, AWS, and distributed systems experience"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resume Writer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Takes that analysis and rewrites the resume to match&lt;/td&gt;
&lt;td&gt;A tailored resume highlighting the right skills&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cover Letter Writer&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Uses both outputs to write a personalized letter&lt;/td&gt;
&lt;td&gt;A cover letter that connects your experience to their needs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each agent does one thing. Together, they handle the full workflow.&lt;/p&gt;




&lt;h3&gt;
  
  
  How Do They Pass Work to Each Other?
&lt;/h3&gt;

&lt;p&gt;Three patterns. You only need to know the first one for now.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Sequential - Like a relay race A→B→C&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Agent 1 finishes → hands output to Agent 2 → hands to Agent 3.&lt;/p&gt;

&lt;p&gt;This is what we'll use. It's the simplest. Easy to understand, easy to fix when something breaks you know exactly which agent caused the problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Hierarchical - Like a manager assigning tasks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One "boss" agent gives work to others and collects results. Useful when you need coordination, but more complex to set up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Parallel - Everyone works at the same time&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Multiple agents work on independent tasks simultaneously. Faster, but the tasks can't depend on each other. Example: three agents searching three different job boards at the same time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For this series, we're using sequential.&lt;/strong&gt; Agent 1 passes to Agent 2 passes to Agent 3. Simple, predictable, easy to debug. You can explore the other patterns later once you're comfortable.&lt;/p&gt;

&lt;p&gt;Here's how our 3 agents work together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────┐       ┌───────────────┐       ┌─────────────────────┐
│ Job Analyzer │──────▶│ Resume Writer │──────▶│ Cover Letter Writer │
│              │       │               │       │                     │
│ "They want   │       │ Rewrites your │       │ Writes a letter     │
│  Python, AWS,│       │ resume to     │       │ connecting your     │
│  distributed │       │ highlight     │       │ experience to       │
│  systems"    │       │ those skills  │       │ their needs         │
└──────────────┘       └───────────────┘       └─────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  CrewAI - The Framework We'll Use
&lt;/h2&gt;

&lt;p&gt;I tried several agent frameworks. I picked CrewAI because it's simple and respects your time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why CrewAI?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free and open source&lt;/strong&gt; - active community, regular updates&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ready-made templates&lt;/strong&gt; - gives you a working project structure immediately&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plain English configuration&lt;/strong&gt; - define agents and tasks in YAML (simple text files), not complex code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast to start&lt;/strong&gt; - first working agent in about 10 minutes once your environment is set up&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Project Structure
&lt;/h3&gt;

&lt;p&gt;When you create a CrewAI project, you get this folder structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-agent-project/
├── src/
│   └── my_agent_project/
│       ├── config/
│       │   ├── agents.yaml      ← WHO: define your agents here
│       │   └── tasks.yaml       ← WHAT: define tasks here
│       ├── tools/
│       │   └── custom_tool.py   ← HOW: custom tools agents can use
│       ├── crew.py              ← WIRING: connects agents to tasks
│       └── main.py              ← START: entry point, kicks everything off
├── knowledge/                   ← FACTS: static files agents can read
├── .env                         ← SECRETS: API keys, model settings
└── pyproject.toml               ← DEPENDENCIES: Python packages needed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;You only need to write three things:&lt;/strong&gt; agent definitions, task definitions, and the wiring between them. Everything else is scaffolding that CrewAI handles.&lt;/p&gt;




&lt;h3&gt;
  
  
  CrewAI vs LangChain - When to Use Which
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;CrewAI&lt;/th&gt;
&lt;th&gt;LangChain/LangGraph&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Approach&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Define agents in simple YAML files&lt;/td&gt;
&lt;td&gt;Build custom chains and graphs in code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Learning time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A weekend&lt;/td&gt;
&lt;td&gt;A week or more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Setup time&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~10 minutes&lt;/td&gt;
&lt;td&gt;30+ minutes of boilerplate&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Best for&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Clear roles, clear handoffs&lt;/td&gt;
&lt;td&gt;Complex branching, conditional logic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Feels like&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Writing job descriptions for a team&lt;/td&gt;
&lt;td&gt;Building a flowchart with many "if/else" paths&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Use CrewAI&lt;/strong&gt; when you know your agents and the flow is straightforward. Agent A does X, passes to Agent B which does Y. Done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use LangChain/LangGraph&lt;/strong&gt; when you need complex logic like "if confidence is above 80% go to Agent A, otherwise retry with Agent B, and if that fails ask a human."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My take:&lt;/strong&gt; Start with CrewAI. You can always add complexity later. Removing complexity? That's painful.&lt;/p&gt;




&lt;h2&gt;
  
  
  MCP - How Agents Talk to the Outside World
&lt;/h2&gt;

&lt;p&gt;Your agent can think. But can it actually reach out and grab information from somewhere else? Like a database full of job listings? Or a file stored in the cloud?&lt;/p&gt;

&lt;p&gt;That's what MCP does. It gives your agent a way to connect to outside services.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Simplest Way to Understand MCP
&lt;/h3&gt;

&lt;p&gt;Imagine you buy a new phone. You need to charge it. Now imagine every phone brand had a completely different charger one for Samsung, one for Apple, one for OnePlus, all different shapes.&lt;/p&gt;

&lt;p&gt;That's what life was like before USB-C. Now? One cable works for almost everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MCP is the USB-C of AI agents.&lt;/strong&gt; One standard way to connect to any service databases, file storage, APIs, messaging apps. You don't write different connection code for each one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Back to Our Resume Agent
&lt;/h3&gt;

&lt;p&gt;Our agent needs job listings. Where do those come from?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maybe a database with thousands of job posts&lt;/li&gt;
&lt;li&gt;Maybe files stored in the cloud (like AWS S3)&lt;/li&gt;
&lt;li&gt;Maybe a job board API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Without MCP:&lt;/strong&gt; You write separate connection code for each one. Different code for the database. Different code for S3. Different code for the API. Every new service = more custom code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With MCP:&lt;/strong&gt; You configure the connection once in a simple settings file. The agent uses the same standard method to talk to all of them. New service? Just add another configuration. No new code.&lt;/p&gt;

&lt;p&gt;The first time I set this up, it took me about 5 minutes. Writing custom database connection code used to take me an hour. That's the difference.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Else Can Agents Connect To?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Databases&lt;/strong&gt; - fetch job listings, store results&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File Storage (S3)&lt;/strong&gt; - read resumes, save tailored versions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt; - read code, create pull requests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slack&lt;/strong&gt; - send notifications, read messages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Search&lt;/strong&gt; - find information online&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One standard way to connect to all of these. That's MCP.&lt;/p&gt;

&lt;p&gt;We'll set this up hands-on in the next article. For now, just remember: MCP is how your agent reaches out to the world beyond its own code.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Agents Can't Do Well (Yet)
&lt;/h2&gt;

&lt;p&gt;Let me be honest about the limitations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;They make things up.&lt;/strong&gt; I've had an agent tell me it "successfully completed" something it definitely didn't do. Always check important outputs yourself.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;They cost money.&lt;/strong&gt; One task = 5-20 AI model calls. A chatbot costs pennies. An agent can cost dollars per run. Multiply by hundreds of runs and your bill gets serious.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;They get stuck in loops.&lt;/strong&gt; Without a maximum attempt limit, an agent will happily retry the same failing approach forever. (I learned this the hard way at 2 AM.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same input, slightly different output.&lt;/strong&gt; This makes testing tricky because you can't always predict the exact result.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;They're slow.&lt;/strong&gt; Multiple AI calls + tool usage = seconds to minutes per task. Not milliseconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;They "fix" things you didn't ask about.&lt;/strong&gt; When fixing one issue, an agent sometimes decides to also change nearby code. In a demo, fine. In production, you get unexpected changes. Set tight boundaries.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  How I Handle These Problems
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use cheap models for simple tasks.&lt;/strong&gt; Amazon Nova Lite for easy stuff, Nova Pro for complex reasoning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep prompts short.&lt;/strong&gt; Every word costs money.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always set max attempts.&lt;/strong&gt; Non-negotiable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Log everything.&lt;/strong&gt; I send all agent activity to CloudWatch Logs token counts, step durations, success/failure at each step. When something breaks at step 5 of 7, I can trace exactly what went wrong.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate outputs.&lt;/strong&gt; For critical tasks, I add a checking step either another agent that verifies the work, or structured checks that confirm the output meets requirements.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  When NOT to Use Agents
&lt;/h2&gt;

&lt;p&gt;Not everything needs an agent. I've seen people over-engineer simple problems because "agents are cool." Skip agents when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One AI call is enough&lt;/strong&gt; - Summarize this text? Translate this paragraph? That's one API call, not an agent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The logic never changes&lt;/strong&gt; - If there are no decisions to make and the steps are always the same, a simple script is cheaper, faster, and more reliable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need instant responses&lt;/strong&gt; - Agents take seconds to minutes. If you need millisecond responses for a real-time API, agents are too slow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High volume + simple task&lt;/strong&gt; - 5-20 AI calls per task × 10,000 tasks per day = a very expensive month. If the task is simple and repetitive, write a rule-based system.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mistakes are unacceptable&lt;/strong&gt; - Agents make errors. If a wrong answer has serious consequences (medical, legal, financial), don't rely on an agent without heavy human oversight.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's just a database query&lt;/strong&gt; - Reading from a database and returning results? That's a simple API, not an agent.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;My rule:&lt;/strong&gt; If you can draw the logic as a simple flowchart with no "it depends" decisions, you don't need an agent. Use a script. Agents are valuable when there's ambiguity, judgment calls, and multi-step reasoning involved.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;In the next article, I'm building the resume tailor agent step by step using CrewAI on an EC2 instance with Amazon Nova Pro as the AI model. From setting up the server to running the agent against a real job description.&lt;/p&gt;

&lt;p&gt;Stay tuned.&lt;/p&gt;




&lt;h2&gt;
  
  
  📌 Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Thanks for reading! If this was helpful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❤️ Like if it added value&lt;/li&gt;
&lt;li&gt;💾 Save for later&lt;/li&gt;
&lt;li&gt;🔄 Share with your team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Follow me for more on:&lt;/strong&gt; AWS architecture, FinOps, DevOps, and AI Infrastructure.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;Visit my website&lt;/a&gt;&lt;/strong&gt; | &lt;strong&gt;&lt;a href="https://www.linkedin.com/in/sarvar04/" rel="noopener noreferrer"&gt;Connect on LinkedIn&lt;/a&gt;&lt;/strong&gt; | &lt;strong&gt;Email:&lt;/strong&gt; &lt;a href="mailto:simplynadaf@gmail.com"&gt;simplynadaf@gmail.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Happy Learning&lt;/strong&gt; 🚀&lt;/p&gt;

</description>
      <category>aws</category>
      <category>ai</category>
      <category>agents</category>
      <category>discuss</category>
    </item>
    <item>
      <title>I Installed Kiro CLI on EC2: Here's Why My First Attempt Failed</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Fri, 15 May 2026 04:37:18 +0000</pubDate>
      <link>https://dev.to/aws-builders/i-installed-kiro-cli-on-ec2-heres-why-my-first-attempt-failed-38hb</link>
      <guid>https://dev.to/aws-builders/i-installed-kiro-cli-on-ec2-heres-why-my-first-attempt-failed-38hb</guid>
      <description>&lt;p&gt;👋 Hey there, tech enthusiasts! &lt;/p&gt;

&lt;p&gt;I'm Sarvar, a Cloud Architect with a passion for transforming complex technological challenges into elegant solutions. With extensive experience spanning Cloud Operations (AWS &amp;amp; Azure), Data Operations, Analytics, DevOps, and Generative AI, I've had the privilege of architecting solutions for global enterprises that drive real business impact. Through this article series, I'm excited to share practical insights, best practices, and hands-on experiences from my journey in the tech world. Whether you're a seasoned professional or just starting out, I aim to break down complex concepts into digestible pieces that you can apply in your projects.&lt;/p&gt;

&lt;p&gt;Let's dive in and explore the fascinating world of cloud technology together! 🚀&lt;/p&gt;




&lt;p&gt;I've been using Amazon Q CLI for nearly two years. It became part of my daily workfloquick infrastructure lookups, generating boilerplate, debugging at odd hours. It was reliable, familiar, and I never thought about replacing it.&lt;/p&gt;

&lt;p&gt;Then the end-of-support announcement dropped.&lt;/p&gt;

&lt;p&gt;At first, I felt that familiar frustration. Another tool sunset. Another migration. But then I paused. Instead of scrambling for a 1:1 replacement, I asked myself: &lt;em&gt;what do I actually need from a terminal AI in 2026?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;My answer wasn't "the same thing I had." It was more. I wanted something that understood my team's standards without me repeating them. Something that could pull real AWS data not just guess at it. Something I could shape into different personas for different tasks.&lt;/p&gt;

&lt;p&gt;That's when I discovered that Kiro which I'd only known as an IDE also ships a CLI. And it's not just a CLI. It's the tool I didn't know I was waiting for.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Isn't Just Another Migration
&lt;/h2&gt;

&lt;p&gt;Let me be clear: I'm not switching because I have to. I'm switching because Kiro CLI solves problems Q CLI never addressed.&lt;/p&gt;

&lt;p&gt;Here's what changed my mind in the first 30 minutes:&lt;/p&gt;

&lt;h3&gt;
  
  
  🧠 Custom Agents
&lt;/h3&gt;

&lt;p&gt;Define specialized personas with restricted tool access, scoped file permissions, and custom prompts. I now have a DevOps agent that can only touch infrastructure files, and a reviewer agent that's physically incapable of modifying anything. Different tools for different jobs from the same CLI.&lt;/p&gt;

&lt;h3&gt;
  
  
  📏 Steering Files
&lt;/h3&gt;

&lt;p&gt;Persistent rules that shape every response. My team's infrastructure standards Graviton instances, GP3 volumes, mandatory tags, Lambda best practices are baked into every interaction automatically. No more repeating "use ARM64" in every prompt.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔌 MCP Server Ecosystem
&lt;/h3&gt;

&lt;p&gt;Connect to AWS Cost Explorer, Pricing API, CloudTrail, Well-Architected tools, and more. This isn't "AI guessing at your costs." This is real data flowing into real recommendations. Within my first week, it found a forgotten NAT Gateway costing $180/month.&lt;/p&gt;

&lt;h3&gt;
  
  
  📋 Planning Mode
&lt;/h3&gt;

&lt;p&gt;Press &lt;code&gt;Shift+Tab&lt;/code&gt; and Kiro switches from "do the thing" to "let's think about the thing first." Structured requirements gathering before implementation. Essential for anything touching multiple services.&lt;/p&gt;

&lt;h3&gt;
  
  
  📊 Real-Time Usage Tracking
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;/usage&lt;/code&gt; shows exactly where you stand credits consumed, tier limits, what's left. No end-of-month surprises.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔐 Multiple Auth Options
&lt;/h3&gt;

&lt;p&gt;Builder ID, IAM Identity Center, GitHub, Google. Teams get centralized billing and access control. Solo developers get flexibility.&lt;/p&gt;

&lt;p&gt;Amazon Q CLI was a good conversational assistant. Kiro CLI is an extensible development platform that happens to live in your terminal.&lt;/p&gt;

&lt;p&gt;That distinction is why I'm writing this guide instead of a "how to cope with deprecation" post.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Quick Comparison
&lt;/h2&gt;

&lt;p&gt;For those coming from Q CLI, here's what's different at a glance:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Amazon Q CLI&lt;/th&gt;
&lt;th&gt;Kiro CLI&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Auth options&lt;/td&gt;
&lt;td&gt;Builder ID, IAM Identity Center&lt;/td&gt;
&lt;td&gt;+ GitHub, Google&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Entry point&lt;/td&gt;
&lt;td&gt;&lt;code&gt;q chat&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;kiro-cli chat&lt;/code&gt; (&lt;code&gt;q&lt;/code&gt; still works)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model selection&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Auto-selects per task&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost visibility&lt;/td&gt;
&lt;td&gt;End of month surprise&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/usage&lt;/code&gt; command, real-time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom agents&lt;/td&gt;
&lt;td&gt;Basic&lt;/td&gt;
&lt;td&gt;Full tool/path scoping, hooks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Steering/Rules&lt;/td&gt;
&lt;td&gt;Simple rules&lt;/td&gt;
&lt;td&gt;Rich markdown standards files&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP ecosystem&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;td&gt;20+ official AWS MCP servers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Planning mode&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;✅ Structured requirements first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;Apache 2.0&lt;/td&gt;
&lt;td&gt;AWS IP License&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The license change matters if your org has strict open-source requirements. For most of us, it doesn't affect day-to-day use.&lt;/p&gt;




&lt;h2&gt;
  
  
  Infrastructure Requirements
&lt;/h2&gt;

&lt;p&gt;Before installing, make sure your machine meets these specs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Minimum&lt;/th&gt;
&lt;th&gt;Recommended&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Architecture&lt;/td&gt;
&lt;td&gt;x86_64 or ARM64&lt;/td&gt;
&lt;td&gt;Either works fine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OS&lt;/td&gt;
&lt;td&gt;Ubuntu, Fedora, or Amazon Linux 2023&lt;/td&gt;
&lt;td&gt;Ubuntu 22.04 LTS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;glibc&lt;/td&gt;
&lt;td&gt;2.34+&lt;/td&gt;
&lt;td&gt;2.35+ (ships with Ubuntu 22.04)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAM&lt;/td&gt;
&lt;td&gt;2 GB&lt;/td&gt;
&lt;td&gt;4 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU&lt;/td&gt;
&lt;td&gt;2 vCPUs&lt;/td&gt;
&lt;td&gt;2 vCPUs is fine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disk&lt;/td&gt;
&lt;td&gt;5 GB free&lt;/td&gt;
&lt;td&gt;10 GB+&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network&lt;/td&gt;
&lt;td&gt;Internet access&lt;/td&gt;
&lt;td&gt;Stable connection&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If your glibc is older than 2.34 (check with &lt;code&gt;ldd --version&lt;/code&gt;), grab the musl version instead it's the one with &lt;code&gt;-musl.zip&lt;/code&gt; in the filename.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Setup
&lt;/h2&gt;

&lt;p&gt;I'm running this on an EC2 instance:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Instance Type&lt;/td&gt;
&lt;td&gt;t3.medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OS&lt;/td&gt;
&lt;td&gt;Ubuntu 22.04.5 LTS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Architecture&lt;/td&gt;
&lt;td&gt;x86_64&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vCPUs&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RAM&lt;/td&gt;
&lt;td&gt;4 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;50 GB GP3&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Region&lt;/td&gt;
&lt;td&gt;us-east-1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I went with t3.medium (4 GB RAM) to keep things comfortable. AI model interactions benefit from headroom, and the cost difference is negligible for a development tool you use daily.&lt;/p&gt;




&lt;h2&gt;
  
  
  Installation (Under 5 Minutes)
&lt;/h2&gt;

&lt;p&gt;The whole process is straightforward. Nine steps, no surprises.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Download Kiro CLI
&lt;/h3&gt;

&lt;p&gt;SSH into your instance and pull the package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--proto&lt;/span&gt; &lt;span class="s1"&gt;'=https'&lt;/span&gt; &lt;span class="nt"&gt;--tlsv1&lt;/span&gt;.2 &lt;span class="nt"&gt;-sSf&lt;/span&gt; &lt;span class="s1"&gt;'https://desktop-release.q.us-east-1.amazonaws.com/latest/kirocli-x86_64-linux.zip'&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="s1"&gt;'kirocli.zip'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fcpern8zxbbaizbq48hm8.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%2Fcpern8zxbbaizbq48hm8.png" alt=" " width="800" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Takes about 10 seconds on a decent connection. The file is around 350 MB.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Extract It
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;unzip kirocli.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fwax0rekz55p2srq39x22.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%2Fwax0rekz55p2srq39x22.png" alt=" " width="800" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Run the Installer
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./kirocli/install.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F1x1l3ll6tpigkzc5axlw.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%2F1x1l3ll6tpigkzc5axlw.png" alt=" " width="799" height="70"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Let It Configure Your Shell
&lt;/h3&gt;

&lt;p&gt;It'll ask:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Do you want kiro-cli to modify your shell config (you will have to manually do this otherwise)?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type &lt;code&gt;yes&lt;/code&gt;. This adds the necessary PATH entries so you can run &lt;code&gt;kiro-cli&lt;/code&gt; from anywhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Authenticate
&lt;/h3&gt;

&lt;p&gt;Now it needs to know who you are. I'm using &lt;strong&gt;Builder ID&lt;/strong&gt; it's free, quick to set up, and works well for individual use. You can also use IAM Identity Center for teams, or GitHub/Google if you prefer.&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%2Fbyp431nvftipim3w6bpt.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%2Fbyp431nvftipim3w6bpt.png" alt=" " width="798" height="110"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Open the Auth URL
&lt;/h3&gt;

&lt;p&gt;Kiro generates a URL for you. Copy it and open it in your browser.&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%2Fya5e23t7fa7ctbbzkhb1.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%2Fya5e23t7fa7ctbbzkhb1.png" alt=" " width="800" height="131"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Log in with your email and password:&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%2Fjdvuonqa3sfrme6j9dpb.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%2Fjdvuonqa3sfrme6j9dpb.png" alt=" " width="800" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7: Confirm Access
&lt;/h3&gt;

&lt;p&gt;Click &lt;strong&gt;Confirm and Continue&lt;/strong&gt;:&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%2Fwd13kzeh1pqhoxzacgsf.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%2Fwd13kzeh1pqhoxzacgsf.png" alt=" " width="691" height="825"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then click &lt;strong&gt;Allow&lt;/strong&gt; when it asks for data access:&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%2Fu3ciszj24xjmz3jawsrt.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%2Fu3ciszj24xjmz3jawsrt.png" alt=" " width="800" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The browser tab closes automatically. You're done with the web part.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 8: Back to the Terminal
&lt;/h3&gt;

&lt;p&gt;You should see a success message:&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%2Fb8mdgmg8hqqsyrcu2u1t.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%2Fb8mdgmg8hqqsyrcu2u1t.png" alt=" " width="727" height="726"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 9: Verify It Works
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kiro-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fcqe1sqq8i6k7uqrbpz7j.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%2Fcqe1sqq8i6k7uqrbpz7j.png" alt=" " width="799" height="160"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you see the Kiro interface, you're in:&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%2Fbs2yjw5yts60tbnbogtd.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%2Fbs2yjw5yts60tbnbogtd.png" alt=" " width="799" height="219"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it. Under five minutes from download to working CLI.&lt;/p&gt;




&lt;h2&gt;
  
  
  Automatic Migration from Q CLI
&lt;/h2&gt;

&lt;p&gt;This is the part that made me smile. When Kiro CLI installed, it automatically detected and migrated my existing Q CLI setup:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Agents&lt;/strong&gt; from &lt;code&gt;~/.aws/amazonq&lt;/code&gt; → &lt;code&gt;~/.kiro/agents/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP config&lt;/strong&gt; from &lt;code&gt;~/.aws/amazonq/mcp.json&lt;/code&gt; → &lt;code&gt;~/.kiro/settings/mcp.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rules&lt;/strong&gt; from &lt;code&gt;~/.aws/amazonq/rules&lt;/code&gt; → &lt;code&gt;~/.kiro/steering/&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Two years of custom configurations carried over in seconds. The only thing I had to fix was one MCP server with a hardcoded path to &lt;code&gt;~/.aws/amazonq/&lt;/code&gt; in its environment variables. Everything else just worked.&lt;/p&gt;

&lt;p&gt;No starting from scratch. No rebuilding your setup. That's how migrations should feel.&lt;/p&gt;




&lt;h2&gt;
  
  
  One More Thing: Classic Mode
&lt;/h2&gt;

&lt;p&gt;By default, Kiro opens in the new Terminal UI. It looks polished, but on remote servers and SSH sessions, I prefer classic mode faster rendering, no UI lag, works perfectly in tmux.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try it once:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kiro-cli &lt;span class="nt"&gt;--classic&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F1hsjjiec5ochez1fwg92.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%2F1hsjjiec5ochez1fwg92.png" alt=" " width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Make it permanent:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kiro-cli settings chat.ui &lt;span class="s2"&gt;"classic"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fk00xbsrzu060zwpedz8p.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%2Fk00xbsrzu060zwpedz8p.png" alt=" " width="797" height="97"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now every time you run &lt;code&gt;kiro-cli&lt;/code&gt;, it starts in classic mode:&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%2Fapk3n8pvp321mfjpw42w.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%2Fapk3n8pvp321mfjpw42w.png" alt=" " width="799" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why I Prefer Classic Mode on Remote Servers
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Faster rendering over SSH&lt;/li&gt;
&lt;li&gt;No UI lag&lt;/li&gt;
&lt;li&gt;Works perfectly in tmux&lt;/li&gt;
&lt;li&gt;More predictable behavior in long sessions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you ever want the full UI back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kiro-cli settings chat.ui &lt;span class="s2"&gt;"tui"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or just run &lt;code&gt;kiro-cli --tui&lt;/code&gt; for a one-off session.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cleanup (Optional)
&lt;/h2&gt;

&lt;p&gt;That 350 MB zip file is still sitting there. Once you've verified everything works:&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;rm&lt;/span&gt; ~/kirocli.zip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  First Impressions After One Week
&lt;/h2&gt;

&lt;p&gt;After seven days of daily use, here's what stood out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;q&lt;/code&gt; command still works.&lt;/strong&gt; Muscle memory preserved. Kiro CLI responds to both &lt;code&gt;kiro-cli&lt;/code&gt; and &lt;code&gt;q&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model auto-selection is smart.&lt;/strong&gt; Simple questions get fast responses. Complex architecture questions get deeper reasoning. I don't have to think about which model to pick.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP servers change everything.&lt;/strong&gt; Going from "AI that guesses" to "AI that queries real data" is a paradigm shift. Cost optimization alone justified the switch.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Steering files are addictive.&lt;/strong&gt; Once you teach it your standards, you can't go back to repeating yourself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I went from "reluctant migrator" to "why didn't this exist sooner" in about three days.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Now that it's installed, the real value starts. In the next article, I'll cover how I use Kiro CLI for actual work setting up MCP servers for cost optimization, creating custom agents that enforce team standards, writing steering files, and the daily workflows that made this tool indispensable.&lt;/p&gt;

&lt;p&gt;👉 Part 2: From Budget Alerts to AI-Powered Optimization The Complete Kiro CLI Guide&lt;/p&gt;




&lt;h2&gt;
  
  
  📌 Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Thank you for reading! I hope this article gave you practical insights and a clearer perspective on the topic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Was this helpful?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❤️ Like if it added value&lt;/li&gt;
&lt;li&gt;🦄 Unicorn if you’re applying it today&lt;/li&gt;
&lt;li&gt;💾 Save for your next optimization session&lt;/li&gt;
&lt;li&gt;🔄 Share with your team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Follow me for more on:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS architecture patterns&lt;/li&gt;
&lt;li&gt;FinOps automation&lt;/li&gt;
&lt;li&gt;Multi-account strategies&lt;/li&gt;
&lt;li&gt;AI-driven DevOps&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💡 What’s Next
&lt;/h2&gt;

&lt;p&gt;More deep dives coming soon on cloud operations, GenAI, Agentic-AI, DevOps, and data workflows follow for weekly insights.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌐 Portfolio &amp;amp; Work
&lt;/h2&gt;

&lt;p&gt;You can explore my full body of work, certifications, architecture projects, and technical articles here:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;Visit My Website&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Services I Offer
&lt;/h2&gt;

&lt;p&gt;If you're looking for hands-on guidance or collaboration, I provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud Architecture Consulting (AWS / Azure)&lt;/li&gt;
&lt;li&gt;DevSecOps &amp;amp; Automation Design&lt;/li&gt;
&lt;li&gt;FinOps Optimization Reviews&lt;/li&gt;
&lt;li&gt;Technical Writing (Cloud, DevOps, GenAI)&lt;/li&gt;
&lt;li&gt;Product &amp;amp; Architecture Reviews&lt;/li&gt;
&lt;li&gt;Mentorship &amp;amp; 1:1 Technical Guidance&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🤝 Let’s Connect
&lt;/h2&gt;

&lt;p&gt;I’d love to hear your thoughts drop a comment or connect with me on &lt;a href="https://www.linkedin.com/in/sarvar04/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For collaborations, consulting, or technical discussions, feel free to reach out directly at &lt;a href="mailto:simplynadaf@gmail.com"&gt;simplynadaf@gmail.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Happy Learning&lt;/strong&gt; 🚀&lt;/p&gt;

</description>
      <category>aws</category>
      <category>ai</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>Deploy Web Servers with Terraform: EC2 + Load Balancer Tutorial</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Mon, 27 Apr 2026 09:40:05 +0000</pubDate>
      <link>https://dev.to/aws-builders/deploy-web-servers-with-terraform-ec2-load-balancer-tutorial-304k</link>
      <guid>https://dev.to/aws-builders/deploy-web-servers-with-terraform-ec2-load-balancer-tutorial-304k</guid>
      <description>&lt;p&gt;👋 Hey there, tech enthusiasts! &lt;/p&gt;

&lt;p&gt;I'm Sarvar, a Cloud Architect with a passion for transforming complex technological challenges into elegant solutions. With extensive experience spanning Cloud Operations (AWS &amp;amp; Azure), Data Operations, Analytics, DevOps, and Generative AI, I've had the privilege of architecting solutions for global enterprises that drive real business impact. Through this article series, I'm excited to share practical insights, best practices, and hands-on experiences from my journey in the tech world. Whether you're a seasoned professional or just starting out, I aim to break down complex concepts into digestible pieces that you can apply in your projects.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"A single server is a single point of failure. A load balancer is your insurance policy."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  🎯 Welcome Back!
&lt;/h2&gt;

&lt;p&gt;Remember in &lt;a href="https://dev.to/aws-builders/building-your-first-vpc-aws-networking-with-terraform-3h34"&gt;Article 6&lt;/a&gt; when you built your first VPC with public and private subnets? You created the network foundation, but it was empty—no servers, no applications, nothing running.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here's the reality:&lt;/strong&gt; A VPC without compute resources is like building a highway with no cars. You need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Web servers to run your applications&lt;/li&gt;
&lt;li&gt;A way to handle traffic spikes&lt;/li&gt;
&lt;li&gt;Protection against server failures&lt;/li&gt;
&lt;li&gt;Zero-downtime deployments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;That's where EC2 instances and Load Balancers come in.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;By the end of this article, you'll:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Deploy EC2 instances with Terraform&lt;/li&gt;
&lt;li&gt;✅ Configure security groups for web servers&lt;/li&gt;
&lt;li&gt;✅ Use user data to automate server setup&lt;/li&gt;
&lt;li&gt;✅ Create an Application Load Balancer (ALB)&lt;/li&gt;
&lt;li&gt;✅ Implement health checks and target groups&lt;/li&gt;
&lt;li&gt;✅ Build high-availability web infrastructure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Time Required:&lt;/strong&gt; 45 minutes (20 min read + 25 min practice)&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Cost:&lt;/strong&gt; ~$33/month (~$16 with free tier for ALB)&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Difficulty:&lt;/strong&gt; Intermediate&lt;/p&gt;

&lt;p&gt;Let's deploy some real infrastructure! 🚀&lt;/p&gt;


&lt;h2&gt;
  
  
  💔 The Problem: Single Server Syndrome
&lt;/h2&gt;
&lt;h3&gt;
  
  
  The Nightmare Scenario
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;It's 2 AM. Your phone rings.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Monitoring Alert: Website Down
Status: 503 Service Unavailable
Cause: EC2 instance crashed
Impact: 100% of users affected
Revenue Loss: $500/minute
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;You scramble to:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;SSH into the server (if you can)&lt;/li&gt;
&lt;li&gt;Restart the application&lt;/li&gt;
&lt;li&gt;Hope it comes back up&lt;/li&gt;
&lt;li&gt;Watch users leave your site&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;The root cause?&lt;/strong&gt; You had &lt;strong&gt;one server&lt;/strong&gt; running everything.&lt;/p&gt;
&lt;h3&gt;
  
  
  Common Single-Server Problems:
&lt;/h3&gt;

&lt;p&gt;❌ &lt;strong&gt;Traffic Spike:&lt;/strong&gt; Black Friday hits, server crashes from load&lt;br&gt;&lt;br&gt;
❌ &lt;strong&gt;Hardware Failure:&lt;/strong&gt; AWS instance dies, site goes down&lt;br&gt;&lt;br&gt;
❌ &lt;strong&gt;Deployment Risk:&lt;/strong&gt; Update breaks something, entire site offline&lt;br&gt;&lt;br&gt;
❌ &lt;strong&gt;No Redundancy:&lt;/strong&gt; One point of failure = business risk&lt;br&gt;&lt;br&gt;
❌ &lt;strong&gt;Manual Recovery:&lt;/strong&gt; You're the human load balancer at 2 AM&lt;br&gt;&lt;br&gt;
❌ &lt;strong&gt;Poor User Experience:&lt;/strong&gt; Slow response times, timeouts, errors&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sound familiar?&lt;/strong&gt; Let's fix this with load balancing.&lt;/p&gt;


&lt;h2&gt;
  
  
  🌟 What is a Load Balancer? (Quick Theory)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Simple Definition
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Load Balancer&lt;/strong&gt; = Traffic cop for your servers. It:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Distributes incoming requests across multiple servers&lt;/li&gt;
&lt;li&gt;Monitors server health automatically&lt;/li&gt;
&lt;li&gt;Routes traffic only to healthy servers&lt;/li&gt;
&lt;li&gt;Enables zero-downtime deployments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Think of it like this:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without Load Balancer:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;All Users → Single Server → 💥 Overloaded/Crashed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;With Load Balancer:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Users → Load Balancer → Server 1 (healthy) ✅
                     → Server 2 (healthy) ✅
                     → Server 3 (unhealthy) ❌ (no traffic)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Why You Need This
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario 1: Traffic Spike&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Normal: 100 requests/sec → 2 servers handle it easily&lt;/li&gt;
&lt;li&gt;Black Friday: 10,000 requests/sec → Load balancer distributes across all servers&lt;/li&gt;
&lt;li&gt;Result: Site stays up, users happy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Scenario 2: Server Failure&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server 1 crashes at 2 AM&lt;/li&gt;
&lt;li&gt;Load balancer detects failure in 30 seconds&lt;/li&gt;
&lt;li&gt;Automatically stops sending traffic to Server 1&lt;/li&gt;
&lt;li&gt;Server 2 handles all traffic&lt;/li&gt;
&lt;li&gt;Result: You sleep through the night&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Scenario 3: Deployment&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploy new code to Server 1&lt;/li&gt;
&lt;li&gt;Load balancer keeps sending traffic to Server 2&lt;/li&gt;
&lt;li&gt;Test Server 1, then switch traffic&lt;/li&gt;
&lt;li&gt;Result: Zero downtime deployment&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  📋 Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before starting, make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Completed &lt;a href="https://dev.tolink-to-article-6"&gt;Article 6: Building Your First AWS VPC&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;✅ Terraform installed (v1.0+)&lt;/li&gt;
&lt;li&gt;✅ AWS CLI configured&lt;/li&gt;
&lt;li&gt;✅ Basic understanding of VPC and networking&lt;/li&gt;
&lt;li&gt;✅ An SSH key pair in AWS (or we'll create one)&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  🏗️ What We're Building
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internet
    ↓
Application Load Balancer (ALB)
    ↓
    ├─→ EC2 Instance 1 (Apache)
    └─→ EC2 Instance 2 (Apache)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Architecture Components:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;VPC&lt;/strong&gt; - Our isolated network&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Public Subnet&lt;/strong&gt; - Where our resources live&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Internet Gateway&lt;/strong&gt; - Internet access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Groups&lt;/strong&gt; - Firewall rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EC2 Instances&lt;/strong&gt; - Web servers running Apache&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application Load Balancer&lt;/strong&gt; - Traffic distributor&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Target Group&lt;/strong&gt; - Manages server health&lt;/li&gt;
&lt;/ol&gt;


&lt;h2&gt;
  
  
  📁 Project Structure
&lt;/h2&gt;

&lt;p&gt;Create this folder structure:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;07-ec2-load-balancer/
├── main.tf           # Main infrastructure code
├── variables.tf      # Input variables
├── outputs.tf        # Output values
└── terraform.tfvars  # Variable values
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F2igrdc5u5wq4g90eiyhl.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%2F2igrdc5u5wq4g90eiyhl.png" alt=" " width="800" height="190"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;The complete source code and Terraform configuration used in this article can be found on GitHub:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/simplynadaf" rel="noopener noreferrer"&gt;
        simplynadaf
      &lt;/a&gt; / &lt;a href="https://github.com/simplynadaf/terraform-by-sarvar" rel="noopener noreferrer"&gt;
        terraform-by-sarvar
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Terraform Series
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🚀 Terraform By Sarvar&lt;/h1&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Complete Terraform tutorial series from basics to advanced concepts&lt;/h3&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://www.terraform.io/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/446abdaa5e617237549619105d10cbd99f693a057dc7b366edd5256283015a75/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5465727261666f726d2d76312e31342b2d3632334345343f7374796c653d666f722d7468652d6261646765266c6f676f3d7465727261666f726d266c6f676f436f6c6f723d7768697465" alt="Terraform"&gt;&lt;/a&gt;
&lt;a href="https://aws.amazon.com/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/0b14c31424193ad1651b7422c0c3fd0f5339f3f2632f5ecc3d5313c83e8aeba0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4157532d436c6f75642d4646393930303f7374796c653d666f722d7468652d6261646765266c6f676f3d616d617a6f6e2d617773266c6f676f436f6c6f723d7768697465" alt="AWS"&gt;&lt;/a&gt;
&lt;a href="https://github.com/simplynadaf/terraform-by-sarvar/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/9218332452902d9e542a100d0af126fd3174a116456614d2cf093546a13783db/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d677265656e2e7376673f7374796c653d666f722d7468652d6261646765" alt="License"&gt;&lt;/a&gt;
&lt;a href="https://dev.to/sarvar_04/series/36958" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/ad1f81014cac91cbb305e20c6fb83301ac4992821ede633a6a83bda4379ed118/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6465762e746f2d5365726965732d3041304130413f7374796c653d666f722d7468652d6261646765266c6f676f3d6465762e746f266c6f676f436f6c6f723d7768697465" alt="dev.to"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://dev.to/sarvar_04/series/36963" rel="nofollow"&gt;📖 Read the Series&lt;/a&gt; • &lt;a href="https://sarvarnadaf.com" rel="nofollow noopener noreferrer"&gt;🌐 Portfolio&lt;/a&gt; • &lt;a href="https://www.linkedin.com/in/sarvar04/" rel="nofollow noopener noreferrer"&gt;💼 LinkedIn&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📚 Series Overview&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;This repository contains &lt;strong&gt;ONLY Terraform code examples&lt;/strong&gt; for the &lt;strong&gt;Terraform By Sarvar&lt;/strong&gt; tutorial series.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ IMPORTANT:&lt;/strong&gt; This repo contains only &lt;code&gt;.tf&lt;/code&gt; files and infrastructure code. Articles are published on dev.to, not stored here.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;📖 &lt;strong&gt;Read the series on dev.to:&lt;/strong&gt; &lt;a href="https://dev.to/sarvar_04/series/36963" rel="nofollow"&gt;https://dev.to/sarvar_04/series/36963&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;🎯 What You'll Learn&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;✅ Infrastructure as Code (IaC) fundamentals&lt;/li&gt;
&lt;li&gt;✅ Terraform basics to advanced concepts&lt;/li&gt;
&lt;li&gt;✅ AWS resource provisioning&lt;/li&gt;
&lt;li&gt;✅ Best practices and real-world patterns&lt;/li&gt;
&lt;li&gt;✅ Production-ready configurations&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;📊 Series Progress&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/5d15589585043bcdb6defccdfdd8103a9e0c81e1d32502a3aee5acca8cfe3462/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f41727469636c65732d3725324631302d626c75653f7374796c653d666c61742d737175617265"&gt;&lt;img src="https://camo.githubusercontent.com/5d15589585043bcdb6defccdfdd8103a9e0c81e1d32502a3aee5acca8cfe3462/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f41727469636c65732d3725324631302d626c75653f7374796c653d666c61742d737175617265" alt="Progress"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/ab5bccc65fd0dfc759cb04ffe3446775f8674a09e936f473d7a51abcd3e02713/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5374617475732d4163746976652d737563636573733f7374796c653d666c61742d737175617265"&gt;&lt;img src="https://camo.githubusercontent.com/ab5bccc65fd0dfc759cb04ffe3446775f8674a09e936f473d7a51abcd3e02713/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5374617475732d4163746976652d737563636573733f7374796c653d666c61742d737175617265" alt="Status"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📖 Article Series&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;📘 Foundation (Articles 1-5) ✅ Complete&lt;/h3&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aws-builders/why-every-developer-should-learn-terraform-in-2026-and-how-to-start--4fk0" rel="nofollow"&gt;Introduction to Terraform &amp;amp; IaC&lt;/a&gt; - ✅ Published&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aws-builders/installing-terraform-and-setting-up-your-environment-1j9b" rel="nofollow"&gt;Installation &amp;amp; Setup&lt;/a&gt; - ✅ Published&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aws-builders/your-first-infrastructure-as-code-in-four-commands-46ep" rel="nofollow"&gt;Your First AWS Resource&lt;/a&gt; - ✅ Published&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aws-builders/terraform-state-the-one-file-you-cant-afford-to-lose-33l4" rel="nofollow"&gt;Understanding Terraform State&lt;/a&gt; - ✅ Published&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aws-builders/terraform-variables-and-outputs-making-your-infrastructure-flexible-3ebc" rel="nofollow"&gt;Variables and Outputs&lt;/a&gt; - ✅ Published&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;📗 Real Infrastructure (Articles 6-10)&lt;/h3&gt;

&lt;/div&gt;
&lt;ol start="6"&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aws-builders/building-your-first-vpc-aws-networking-with-terraform-3h34" rel="nofollow"&gt;Building a VPC from Scratch&lt;/a&gt;…&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/simplynadaf/terraform-by-sarvar" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;





&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;p&gt;Follow these steps to run the project on your local machine:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clone the repository:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/simplynadaf/terraform-by-sarvar.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Navigate to the project directory:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;terraform-by-sarvar/articles/08-lambda-serverless
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔧 Step 1: Define Variables
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;variables.tf&lt;/code&gt;:&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="c1"&gt;# AWS Region&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"aws_region"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS region where resources will be created"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&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="c1"&gt;# Project Name&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"project_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Project name for resource naming"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-web"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Environment&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"environment"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Environment name"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# VPC CIDR&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"vpc_cidr"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"CIDR block for VPC"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Public Subnet CIDR&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"public_subnet_cidr"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"CIDR block for public subnet"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.1.0/24"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Instance Type&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"instance_type"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EC2 instance type"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.micro"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Instance Count&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"instance_count"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Number of EC2 instances"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# SSH Key Name&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"key_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SSH key pair name"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-key"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# My IP for SSH Access&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"my_ip"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Your IP address for SSH access (CIDR format)"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&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="c1"&gt;# Change this to your IP for security&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fif5m2cl3bo96xf9a27tn.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%2Fif5m2cl3bo96xf9a27tn.png" alt=" " width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Points:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;instance_count = 2&lt;/code&gt; - We'll create 2 web servers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;instance_type = "t2.micro"&lt;/code&gt; - Free tier eligible&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;my_ip&lt;/code&gt; - Restrict SSH access (change from 0.0.0.0/0 in production!)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🌐 Step 2: Create VPC and Networking
&lt;/h2&gt;

&lt;p&gt;Add to &lt;code&gt;main.tf&lt;/code&gt;:&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="c1"&gt;# Terraform Configuration&lt;/span&gt;
&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 1.0"&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 5.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="c1"&gt;# Provider Configuration&lt;/span&gt;
&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_region&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Data source: Get latest Amazon Linux 2023 AMI&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_ami"&lt;/span&gt; &lt;span class="s2"&gt;"amazon_linux"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;most_recent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;owners&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"amazon"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt;
    &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"al2023-ami-*-x86_64"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"virtualization-type"&lt;/span&gt;
    &lt;span class="nx"&gt;values&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"hvm"&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="c1"&gt;# Data source: Get available availability zones&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_availability_zones"&lt;/span&gt; &lt;span class="s2"&gt;"available"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"available"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# VPC&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_cidr&lt;/span&gt;
  &lt;span class="nx"&gt;enable_dns_hostnames&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;enable_dns_support&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-vpc"&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Internet Gateway&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_internet_gateway"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-igw"&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Public Subnet&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_subnet"&lt;/span&gt; &lt;span class="s2"&gt;"public"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_subnet_cidr&lt;/span&gt;
  &lt;span class="nx"&gt;availability_zone&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_availability_zones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;map_public_ip_on_launch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-public-subnet"&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;Type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Public"&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Public Route Table&lt;/span&gt;

&lt;span class="c1"&gt;# Public Subnet 2 (Different AZ for ALB)&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_subnet"&lt;/span&gt; &lt;span class="s2"&gt;"public_2"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.2.0/24"&lt;/span&gt;
  &lt;span class="nx"&gt;availability_zone&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_availability_zones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;map_public_ip_on_launch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-public-subnet-2"&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;Type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Public"&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route_table"&lt;/span&gt; &lt;span class="s2"&gt;"public"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;route&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_block&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="nx"&gt;gateway_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_internet_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-public-rt"&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;Type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Public"&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Route Table Association&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route_table_association"&lt;/span&gt; &lt;span class="s2"&gt;"public"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;route_table_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_route_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Route Table Association for Public Subnet 2&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route_table_association"&lt;/span&gt; &lt;span class="s2"&gt;"public_2"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;route_table_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_route_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;New Concept: Data Sources&lt;/strong&gt;&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;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_ami"&lt;/span&gt; &lt;span class="s2"&gt;"amazon_linux"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;most_recent&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;owners&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"amazon"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F7lvfs3hi5vec2lll0jxq.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%2F7lvfs3hi5vec2lll0jxq.png" alt=" " width="800" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data sources&lt;/strong&gt; query existing AWS resources. Here we're finding the latest Amazon Linux AMI automatically - no need to hardcode AMI IDs!&lt;/p&gt;




&lt;h2&gt;
  
  
  🔒 Step 3: Configure Security Groups
&lt;/h2&gt;

&lt;p&gt;Security groups are like firewalls. Add to &lt;code&gt;main.tf&lt;/code&gt;:&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="c1"&gt;# Security Group for ALB&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group"&lt;/span&gt; &lt;span class="s2"&gt;"alb"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-alb-sg"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Security group for Application Load Balancer"&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"HTTP from anywhere"&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="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="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="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="nx"&gt;egress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow all outbound traffic"&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;0&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;0&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;"-1"&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="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-alb-sg"&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Security Group for EC2 Instances&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group"&lt;/span&gt; &lt;span class="s2"&gt;"instance"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-instance-sg"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Security group for EC2 instances"&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"HTTP from ALB"&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="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="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="nx"&gt;security_groups&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SSH from my IP"&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="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="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="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="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;my_ip&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;egress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow all outbound traffic"&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;0&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;0&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;"-1"&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="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-instance-sg"&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&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;&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%2Fmv8damni39oo8eticurk.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%2Fmv8damni39oo8eticurk.png" alt=" " width="796" height="505"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security Architecture:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Internet → ALB (Port 80) → EC2 Instances (Port 80)
                            EC2 Instances (Port 22 from your IP)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key Security Practices:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;ALB accepts HTTP (80) from anywhere&lt;/li&gt;
&lt;li&gt;EC2 instances only accept HTTP from ALB (not directly from internet!)&lt;/li&gt;
&lt;li&gt;SSH (22) only from your IP address&lt;/li&gt;
&lt;li&gt;This is called "defense in depth"&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🖥️ Step 4: Deploy EC2 Instances
&lt;/h2&gt;

&lt;p&gt;Now for the exciting part - creating web servers! Add to &lt;code&gt;main.tf&lt;/code&gt;:&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="c1"&gt;# EC2 Instances&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"web"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_count&lt;/span&gt;

  &lt;span class="nx"&gt;ami&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_ami&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amazon_linux&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_type&lt;/span&gt;
  &lt;span class="nx"&gt;key_name&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key_name&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;vpc_security_group_ids&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;user_data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
    #!/bin/bash
    dnf update -y
    dnf install -y httpd
    systemctl start httpd
    systemctl enable httpd
    echo "&amp;lt;h1&amp;gt;Terraform Web Server - Instance: $(ec2-metadata --instance-id | cut -d' ' -f2)&amp;lt;/h1&amp;gt;&amp;lt;p&amp;gt;AZ: $(ec2-metadata --availability-zone | cut -d' ' -f2)&amp;lt;/p&amp;gt;" &amp;gt; /var/www/html/index.html
&lt;/span&gt;&lt;span class="no"&gt;    EOF
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-web-${count.index + 1}"&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
    &lt;span class="nx"&gt;Server&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"web-${count.index + 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;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%2F9r93wxaz9ay4rr4q3dil.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%2F9r93wxaz9ay4rr4q3dil.png" alt=" " width="800" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Breaking Down the Code:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Count Meta-Argument:&lt;/strong&gt;&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;count&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_count&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Creates multiple instances. With &lt;code&gt;instance_count = 2&lt;/code&gt;, we get 2 servers!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. User Data - The Magic:&lt;/strong&gt;&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;user_data&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
  #!/bin/bash
  yum update -y
  yum install -y httpd
  # ...
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;User data runs automatically when the instance starts. It:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Updates the system&lt;/li&gt;
&lt;li&gt;Installs Apache web server&lt;/li&gt;
&lt;li&gt;Starts Apache&lt;/li&gt;
&lt;li&gt;Creates a custom HTML page&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Dynamic Naming:&lt;/strong&gt;&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;Name&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-web-${count.index + 1}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;First instance: &lt;code&gt;terraform-web-web-1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Second instance: &lt;code&gt;terraform-web-web-2&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ⚖️ Step 5: Create Application Load Balancer
&lt;/h2&gt;

&lt;p&gt;The load balancer distributes traffic. Add to &lt;code&gt;main.tf&lt;/code&gt;:&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="c1"&gt;# Application Load Balancer&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lb"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-alb"&lt;/span&gt;
  &lt;span class="nx"&gt;internal&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="nx"&gt;load_balancer_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"application"&lt;/span&gt;
  &lt;span class="nx"&gt;security_groups&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;subnets&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;enable_deletion_protection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-alb"&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Target Group&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lb_target_group"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-tg"&lt;/span&gt;
  &lt;span class="nx"&gt;port&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&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;"HTTP"&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;health_check&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;enabled&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="nx"&gt;healthy_threshold&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="nx"&gt;unhealthy_threshold&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
    &lt;span class="nx"&gt;timeout&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
    &lt;span class="nx"&gt;interval&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;
    &lt;span class="nx"&gt;path&lt;/span&gt;                &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&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;"HTTP"&lt;/span&gt;
    &lt;span class="nx"&gt;matcher&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"200"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-tg"&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Target Group Attachment&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lb_target_group_attachment"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_count&lt;/span&gt;

  &lt;span class="nx"&gt;target_group_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lb_target_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;target_id&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;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="c1"&gt;# ALB Listener&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lb_listener"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;load_balancer_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;port&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"80"&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;"HTTP"&lt;/span&gt;

  &lt;span class="nx"&gt;default_action&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;type&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"forward"&lt;/span&gt;
    &lt;span class="nx"&gt;target_group_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lb_target_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-listener"&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&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;&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%2Ffccbk1qksgt48fu3fh3w.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%2Ffccbk1qksgt48fu3fh3w.png" alt=" " width="798" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understanding Load Balancer Components:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Application Load Balancer (ALB):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The main load balancer resource&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;internal = false&lt;/code&gt; - Internet-facing&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;load_balancer_type = "application"&lt;/code&gt; - Layer 7 (HTTP/HTTPS)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Target Group:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manages the backend servers&lt;/li&gt;
&lt;li&gt;Performs health checks every 30 seconds&lt;/li&gt;
&lt;li&gt;Marks servers healthy after 2 successful checks&lt;/li&gt;
&lt;li&gt;Marks servers unhealthy after 2 failed checks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Target Group Attachment:&lt;/strong&gt;&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;count&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_count&lt;/span&gt;
&lt;span class="nx"&gt;target_id&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_instance&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Registers each EC2 instance with the target group.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Listener:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Listens on port 80&lt;/li&gt;
&lt;li&gt;Forwards traffic to the target group&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Health Check Flow:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ALB → Checks "/" every 30s → Expects HTTP 200 → 
  ✅ Healthy (2 successes) → Receives traffic
  ❌ Unhealthy (2 failures) → No traffic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📤 Step 6: Define Outputs
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;outputs.tf&lt;/code&gt;:&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="c1"&gt;# VPC Outputs&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"vpc_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ID of the VPC"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Subnet Output&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"public_subnet_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ID of public subnet"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# EC2 Instance Outputs&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"instance_ids"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"IDs of EC2 instances"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web&lt;/span&gt;&lt;span class="p"&gt;[*].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"instance_public_ips"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Public IPs of EC2 instances"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web&lt;/span&gt;&lt;span class="p"&gt;[*].&lt;/span&gt;&lt;span class="nx"&gt;public_ip&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Load Balancer Outputs&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"alb_dns_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"DNS name of the Application Load Balancer"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_lb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dns_name&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"alb_url"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"URL to access the load balancer"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"http://${aws_lb.main.dns_name}"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Security Group Outputs&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"alb_security_group_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ID of ALB security group"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"instance_security_group_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ID of instance security group"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fqh3c9hhayj81ajiboh38.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%2Fqh3c9hhayj81ajiboh38.png" alt=" " width="799" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Splat Expression:&lt;/strong&gt;&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;aws_instance&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web&lt;/span&gt;&lt;span class="p"&gt;[*]&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;[*]&lt;/code&gt; gets ALL instance IDs as a list. Super useful with &lt;code&gt;count&lt;/code&gt;!&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚙️ Step 7: Set Variable Values
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;terraform.tfvars&lt;/code&gt;:&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;aws_region&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;span class="nx"&gt;project_name&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-web"&lt;/span&gt;
&lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt;

&lt;span class="c1"&gt;# Network Configuration&lt;/span&gt;
&lt;span class="nx"&gt;vpc_cidr&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&lt;/span&gt;
&lt;span class="nx"&gt;public_subnet_cidr&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.1.0/24"&lt;/span&gt;

&lt;span class="c1"&gt;# EC2 Configuration&lt;/span&gt;
&lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.micro"&lt;/span&gt;
&lt;span class="nx"&gt;instance_count&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="nx"&gt;key_name&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-key"&lt;/span&gt;

&lt;span class="c1"&gt;# Security - Change this to your IP address&lt;/span&gt;
&lt;span class="nx"&gt;my_ip&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Faneab1a2uyh07iikqkvj.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%2Faneab1a2uyh07iikqkvj.png" alt=" " width="799" height="395"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Important:&lt;/strong&gt; Change &lt;code&gt;my_ip&lt;/code&gt; to your actual IP address for security!&lt;/p&gt;

&lt;p&gt;Find your IP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl ifconfig.me
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then update:&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;my_ip&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"203.0.113.0/32"&lt;/span&gt;  &lt;span class="c1"&gt;# Your IP&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🚀 Step 8: Deploy the Infrastructure
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Create SSH Key Pair (if you don't have one)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create key pair in AWS&lt;/span&gt;
aws ec2 create-key-pair &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--key-name&lt;/span&gt; my-key &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'KeyMaterial'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; my-key.pem

&lt;span class="o"&gt;![&lt;/span&gt; &lt;span class="o"&gt;](&lt;/span&gt;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ttadkcyqnuxbsnpwdqs7.png&lt;span class="o"&gt;)&lt;/span&gt;


&lt;span class="c"&gt;# Set permissions&lt;/span&gt;
&lt;span class="nb"&gt;chmod &lt;/span&gt;400 my-key.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F5w5onxtbivy5i7f6eju6.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%2F5w5onxtbivy5i7f6eju6.png" alt=" " width="799" height="307"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Initialize Terraform
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;p&gt;Expected output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Initializing the backend...
Initializing provider plugins...
&lt;/span&gt;&lt;span class="gp"&gt;- Finding hashicorp/aws versions matching "~&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;5.0&lt;span class="s2"&gt;"...
&lt;/span&gt;&lt;span class="go"&gt;- Installing hashicorp/aws v5.100.0...

Terraform has been successfully initialized!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fcugi4ubz4be7o44aclr5.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%2Fcugi4ubz4be7o44aclr5.png" alt=" " width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Review the Plan
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;p&gt;You should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Plan: 14 to add, 0 to change, 0 to destroy.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Resources being created:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 VPC&lt;/li&gt;
&lt;li&gt;1 Internet Gateway&lt;/li&gt;
&lt;li&gt;1 Subnet&lt;/li&gt;
&lt;li&gt;1 Route Table + Association&lt;/li&gt;
&lt;li&gt;2 Security Groups&lt;/li&gt;
&lt;li&gt;2 EC2 Instances&lt;/li&gt;
&lt;li&gt;1 Application Load Balancer&lt;/li&gt;
&lt;li&gt;1 Target Group&lt;/li&gt;
&lt;li&gt;2 Target Group Attachments&lt;/li&gt;
&lt;li&gt;1 ALB Listener&lt;/li&gt;
&lt;/ul&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%2Fz0z59t8hbjhhd3qs515l.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%2Fz0z59t8hbjhhd3qs515l.png" alt=" " width="799" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Apply the Configuration
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;p&gt;Type &lt;code&gt;yes&lt;/code&gt; when prompted.&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%2F2ylek3n7tulgcoz3pwbg.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%2F2ylek3n7tulgcoz3pwbg.png" alt=" " width="778" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This will take 3-5 minutes&lt;/strong&gt; because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EC2 instances need to boot&lt;/li&gt;
&lt;li&gt;User data script runs&lt;/li&gt;
&lt;li&gt;ALB needs to provision&lt;/li&gt;
&lt;li&gt;Health checks need to pass&lt;/li&gt;
&lt;/ul&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%2Fz2guj44wzdot3raki7jz.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%2Fz2guj44wzdot3raki7jz.png" alt=" " width="799" height="382"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Step 9: Test Your Infrastructure
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Get the Load Balancer URL
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://terraform-web-alb-1740709428.us-east-1.elb.amazonaws.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fxmtznr2i4okd2xizw9cc.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%2Fxmtznr2i4okd2xizw9cc.png" alt=" " width="800" height="104"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Test in Browser
&lt;/h3&gt;

&lt;p&gt;Open the URL in your browser. You should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🚀 Terraform Web Server
Deployed with Infrastructure as Code

Instance ID: i-09305fc7d4638360a
Availability Zone: us-east-1a
Server: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fakpjfa84z1su2uavux4a.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%2Fakpjfa84z1su2uavux4a.png" alt=" " width="800" height="155"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Test Load Balancing
&lt;/h3&gt;

&lt;p&gt;Refresh the page multiple times. You'll see the &lt;strong&gt;Instance ID&lt;/strong&gt; and &lt;strong&gt;Server number&lt;/strong&gt; change - that's the load balancer distributing traffic!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;First request  → Server: 1
Second request → Server: 2
Third request  → Server: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F8xsbgk7ketma91mhrisf.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%2F8xsbgk7ketma91mhrisf.png" alt=" " width="800" height="157"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Test Individual Instances
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Get instance IPs&lt;/span&gt;
terraform output instance_public_ips

&lt;span class="o"&gt;![&lt;/span&gt; &lt;span class="o"&gt;](&lt;/span&gt;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jbibepofm64vdjwyk9kj.png&lt;span class="o"&gt;)&lt;/span&gt;


&lt;span class="c"&gt;# Test directly (should work)&lt;/span&gt;
curl http://&amp;lt;instance-ip&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔍 Understanding What Happened
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Traffic Flow
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User Request
    ↓
ALB DNS (terraform-web-alb-xxx.elb.amazonaws.com)
    ↓
ALB Listener (Port 80)
    ↓
Target Group (Health Check: ✅)
    ↓
Round-Robin Distribution
    ├─→ EC2 Instance 1 (Apache) → Response
    └─→ EC2 Instance 2 (Apache) → Response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Health Check Process
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Every 30 seconds:
ALB → GET / → EC2 Instance
    ↓
HTTP 200 OK?
    ├─ Yes (2 times) → Mark Healthy → Send Traffic
    └─ No (2 times)  → Mark Unhealthy → Stop Traffic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🧪 Advanced Testing
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Test High Availability
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; What if one server fails?&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="c"&gt;# Get instance IDs&lt;/span&gt;
terraform output instance_ids

&lt;span class="c"&gt;# Stop one instance&lt;/span&gt;
aws ec2 stop-instances &lt;span class="nt"&gt;--instance-ids&lt;/span&gt; i-xxxxx

&lt;span class="c"&gt;# Wait 1 minute for health check to fail&lt;/span&gt;

&lt;span class="c"&gt;# Test the ALB URL - still works!&lt;/span&gt;
curl http://&amp;lt;alb-dns&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ALB automatically stops sending traffic to the unhealthy instance!&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitor Health Status
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check target health&lt;/span&gt;
aws elbv2 describe-target-health &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target-group-arn&lt;/span&gt; &amp;lt;target-group-arn&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"TargetHealthDescriptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"Target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"i-xxxxx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"Port"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"HealthCheckPort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"80"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"TargetHealth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"State"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"healthy"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🐛 Troubleshooting Common Issues
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Issue 1: "InvalidKeyPair.NotFound"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: creating EC2 Instance: InvalidKeyPair.NotFound
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&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="c"&gt;# List your key pairs&lt;/span&gt;
aws ec2 describe-key-pairs

&lt;span class="c"&gt;# Create if missing&lt;/span&gt;
aws ec2 create-key-pair &lt;span class="nt"&gt;--key-name&lt;/span&gt; my-key &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'KeyMaterial'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; my-key.pem
&lt;span class="nb"&gt;chmod &lt;/span&gt;400 my-key.pem

&lt;span class="c"&gt;# Update terraform.tfvars with correct key name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue 2: ALB Shows "503 Service Unavailable"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; Instances are unhealthy or still booting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&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="c"&gt;# Wait 2-3 minutes for:&lt;/span&gt;
&lt;span class="c"&gt;# 1. Instances to boot&lt;/span&gt;
&lt;span class="c"&gt;# 2. User data to complete&lt;/span&gt;
&lt;span class="c"&gt;# 3. Health checks to pass&lt;/span&gt;

&lt;span class="c"&gt;# Check target health&lt;/span&gt;
aws elbv2 describe-target-health &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--target-group-arn&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;terraform output &lt;span class="nt"&gt;-raw&lt;/span&gt; target_group_arn&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue 3: Can't SSH to Instances
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cause:&lt;/strong&gt; Security group blocks your IP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&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="c"&gt;# Get your current IP&lt;/span&gt;
curl ifconfig.me

&lt;span class="c"&gt;# Update terraform.tfvars&lt;/span&gt;
my_ip &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"YOUR_IP/32"&lt;/span&gt;

&lt;span class="c"&gt;# Apply changes&lt;/span&gt;
terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue 4: "Subnet must have at least 2 availability zones"
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error with ALB:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: creating ELBv2 Load Balancer: ValidationError
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; ALBs require at least 2 subnets in different AZs. Update &lt;code&gt;main.tf&lt;/code&gt;:&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="c1"&gt;# Add second subnet&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_subnet"&lt;/span&gt; &lt;span class="s2"&gt;"public_2"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.2.0/24"&lt;/span&gt;
  &lt;span class="nx"&gt;availability_zone&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_availability_zones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;map_public_ip_on_launch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-public-subnet-2"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Update ALB subnets&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_lb"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
  &lt;span class="nx"&gt;subnets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;h2&gt;
  
  
  💰 Cost Breakdown
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Monthly Costs (us-east-1):&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Quantity&lt;/th&gt;
&lt;th&gt;Cost/Month&lt;/th&gt;
&lt;th&gt;Total&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;t2.micro EC2&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;$8.50&lt;/td&gt;
&lt;td&gt;$17.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application Load Balancer&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;$16.20&lt;/td&gt;
&lt;td&gt;$16.20&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data Transfer (1GB)&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;$0.09&lt;/td&gt;
&lt;td&gt;$0.09&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$33.29&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Free Tier Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First 750 hours/month of t2.micro (covers both instances!)&lt;/li&gt;
&lt;li&gt;First 15 GB data transfer out&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actual cost with free tier: ~$16.20/month&lt;/strong&gt; (just the ALB)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cost Optimization Tips:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use t2.micro (free tier eligible)&lt;/li&gt;
&lt;li&gt;Delete resources when not in use&lt;/li&gt;
&lt;li&gt;Use Network Load Balancer ($10.95/month) if you don't need Layer 7 features&lt;/li&gt;
&lt;li&gt;Consider using Auto Scaling (covered in future articles)&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🧹 Cleanup
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Don't forget to destroy resources to avoid charges!&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="c"&gt;# Destroy everything&lt;/span&gt;
terraform destroy

&lt;span class="c"&gt;# Type 'yes' when prompted&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fgwb740r1s3n1coq8826s.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%2Fgwb740r1s3n1coq8826s.png" alt=" " width="800" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will delete:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Load Balancer&lt;/li&gt;
&lt;li&gt;✅ Target Group&lt;/li&gt;
&lt;li&gt;✅ EC2 Instances&lt;/li&gt;
&lt;li&gt;✅ Security Groups&lt;/li&gt;
&lt;li&gt;✅ VPC and networking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Verify deletion:&lt;/strong&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="c"&gt;# Check EC2 instances&lt;/span&gt;
aws ec2 describe-instances &lt;span class="nt"&gt;--filters&lt;/span&gt; &lt;span class="s2"&gt;"Name=tag:ManagedBy,Values=Terraform"&lt;/span&gt;

&lt;span class="c"&gt;# Check load balancers&lt;/span&gt;
aws elbv2 describe-load-balancers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📚 Key Concepts Learned
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. &lt;strong&gt;Count Meta-Argument&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Creates multiple identical resources. Access with &lt;code&gt;[count.index]&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. &lt;strong&gt;Data Sources&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_ami"&lt;/span&gt; &lt;span class="s2"&gt;"amazon_linux"&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;Query existing AWS resources instead of hardcoding values.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. &lt;strong&gt;User Data&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;user_data&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;-&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
  #!/bin/bash
  # Bootstrap script
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Automate instance configuration at launch.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;strong&gt;Security Group References&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;security_groups&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reference one security group from another for layered security.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. &lt;strong&gt;Splat Expressions&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;aws_instance&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;web&lt;/span&gt;&lt;span class="p"&gt;[*]&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get all values from resources created with &lt;code&gt;count&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. &lt;strong&gt;Health Checks&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;health_check&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;path&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;
  &lt;span class="nx"&gt;interval&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Automatic monitoring and traffic routing.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Real-World Applications
&lt;/h2&gt;

&lt;p&gt;This architecture is used for:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Web Applications&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WordPress sites&lt;/li&gt;
&lt;li&gt;E-commerce platforms&lt;/li&gt;
&lt;li&gt;SaaS applications&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. API Backends&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;REST APIs&lt;/li&gt;
&lt;li&gt;GraphQL servers&lt;/li&gt;
&lt;li&gt;Microservices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Content Delivery&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Static websites&lt;/li&gt;
&lt;li&gt;Media streaming&lt;/li&gt;
&lt;li&gt;File downloads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Development Environments&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Staging servers&lt;/li&gt;
&lt;li&gt;Testing environments&lt;/li&gt;
&lt;li&gt;Demo applications&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🚀 What's Next?
&lt;/h2&gt;

&lt;p&gt;In the next article, we'll add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;RDS Database&lt;/strong&gt; - Persistent data storage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS Secrets Manager&lt;/strong&gt; - Secure credential management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Private Subnets&lt;/strong&gt; - Enhanced security&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database Connection&lt;/strong&gt; - Connect EC2 to RDS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Coming Up:&lt;/strong&gt; &lt;a href="https://dev.tolink"&gt;Article 8: Secure Database Deployment: RDS + Secrets Manager with Terraform&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📖 Additional Resources
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Official Documentation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/instance" rel="noopener noreferrer"&gt;Terraform AWS Provider - EC2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb" rel="noopener noreferrer"&gt;Terraform AWS Provider - ALB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/elasticloadbalancing/latest/application/" rel="noopener noreferrer"&gt;AWS Application Load Balancer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Related Articles:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.tolink"&gt;Article 6: Building Your First AWS VPC with Terraform&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.tolink"&gt;Article 5: Variables and Outputs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/architecture/well-architected/" rel="noopener noreferrer"&gt;AWS Well-Architected Framework&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💬 Questions?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Common Questions:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Can I use different instance types for each server?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A: Yes! Use &lt;code&gt;for_each&lt;/code&gt; instead of &lt;code&gt;count&lt;/code&gt; for more flexibility (covered in Article 13).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: How do I add HTTPS/SSL?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A: You'll need an ACM certificate and update the listener to port 443. We'll cover this in a future article.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Can I deploy to multiple regions?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A: Yes! Use Terraform workspaces or separate state files per region.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: How do I add auto-scaling?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A: Replace EC2 instances with an Auto Scaling Group. We'll cover this in Article 15.&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Summary
&lt;/h2&gt;

&lt;p&gt;Today you learned how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Deploy multiple EC2 instances with Terraform&lt;/li&gt;
&lt;li&gt;✅ Use &lt;code&gt;count&lt;/code&gt; to create multiple resources&lt;/li&gt;
&lt;li&gt;✅ Configure security groups for layered security&lt;/li&gt;
&lt;li&gt;✅ Automate server setup with user data&lt;/li&gt;
&lt;li&gt;✅ Create an Application Load Balancer&lt;/li&gt;
&lt;li&gt;✅ Implement health checks and high availability&lt;/li&gt;
&lt;li&gt;✅ Test load balancing in action&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;You now have production-ready web infrastructure!&lt;/strong&gt; 🎉&lt;/p&gt;




&lt;h2&gt;
  
  
  📌 Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Thank you for reading. I hope this article provided practical insights and a clearer understanding of the topic.&lt;/p&gt;

&lt;p&gt;If you found this useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❤️ Like if it added value
&lt;/li&gt;
&lt;li&gt;🦄 Unicorn if you’re applying it today
&lt;/li&gt;
&lt;li&gt;💾 Save it for your next optimization session
&lt;/li&gt;
&lt;li&gt;🔄 Share it with your team
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💡 What’s Next
&lt;/h2&gt;

&lt;p&gt;More deep dives are coming soon on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud Operations&lt;/li&gt;
&lt;li&gt;GenAI &amp;amp; Agentic AI&lt;/li&gt;
&lt;li&gt;DevOps Automation&lt;/li&gt;
&lt;li&gt;Data &amp;amp; Platform Engineering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow along for weekly insights and hands-on guides.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌐 Portfolio &amp;amp; Work
&lt;/h2&gt;

&lt;p&gt;You can explore my full body of work, certifications, architecture projects, and technical articles here:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;Visit My Website&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Services I Offer
&lt;/h2&gt;

&lt;p&gt;If you're looking for hands-on guidance or collaboration, I provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud Architecture Consulting (AWS / Azure)&lt;/li&gt;
&lt;li&gt;DevSecOps &amp;amp; Automation Design&lt;/li&gt;
&lt;li&gt;FinOps Optimization Reviews&lt;/li&gt;
&lt;li&gt;Technical Writing (Cloud, DevOps, GenAI)&lt;/li&gt;
&lt;li&gt;Product &amp;amp; Architecture Reviews&lt;/li&gt;
&lt;li&gt;Mentorship &amp;amp; 1:1 Technical Guidance&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🤝 Let’s Connect
&lt;/h2&gt;

&lt;p&gt;I’d love to hear your thoughts. Feel free to drop a comment or connect with me on:&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://www.linkedin.com/in/sarvar04/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For collaborations, consulting, or technical discussions, reach out at:&lt;/p&gt;

&lt;p&gt;📧 &lt;a href="mailto:simplynadaf@gmail.com"&gt;simplynadaf@gmail.com&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Found this helpful? Share it with your team.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;⭐ Star the repo • 📖 Follow the series • 💬 Ask questions  &lt;/p&gt;

&lt;p&gt;Made by &lt;strong&gt;Sarvar Nadaf&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
🌐 &lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;https://sarvarnadaf.com&lt;/a&gt;&lt;/p&gt;




</description>
      <category>terraform</category>
      <category>devops</category>
      <category>aws</category>
      <category>infrastructure</category>
    </item>
    <item>
      <title>Stop Giving AI Agents AWS Credentials: A Better Way to Secure Access</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Mon, 20 Apr 2026 14:00:16 +0000</pubDate>
      <link>https://dev.to/aws-builders/stop-giving-ai-agents-aws-credentials-a-better-way-to-secure-access-5gih</link>
      <guid>https://dev.to/aws-builders/stop-giving-ai-agents-aws-credentials-a-better-way-to-secure-access-5gih</guid>
      <description>&lt;p&gt;👋 Hey there, tech enthusiasts! &lt;/p&gt;

&lt;p&gt;I'm Sarvar, a Cloud Architect with a passion for transforming complex technological challenges into elegant solutions. With extensive experience spanning Cloud Operations (AWS &amp;amp; Azure), Data Operations, Analytics, DevOps, and Generative AI, I've had the privilege of architecting solutions for global enterprises that drive real business impact. Through this article series, I'm excited to share practical insights, best practices, and hands-on experiences from my journey in the tech world. Whether you're a seasoned professional or just starting out, I aim to break down complex concepts into digestible pieces that you can apply in your projects.&lt;/p&gt;

&lt;p&gt;Let's dive in and explore the fascinating world of cloud technology together! 🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  The Wake-Up Call
&lt;/h2&gt;

&lt;p&gt;Three months ago, our security team flagged something concerning. Developers were feeding production logs, error messages, and configuration snippets to ChatGPT for debugging help.&lt;/p&gt;

&lt;p&gt;The problem? Those logs contained customer identifiers, internal service names, and architectural details we definitely didn't want leaving our network.&lt;/p&gt;

&lt;p&gt;We couldn't just block ChatGPT - developers needed AI assistance. The productivity gains were real. But we also couldn't keep hemorrhaging sensitive data to external APIs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The requirements were clear:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI agents need AWS access for legitimate automation tasks&lt;/li&gt;
&lt;li&gt;Zero sensitive data leaves our AWS environment&lt;/li&gt;
&lt;li&gt;Every action must be auditable&lt;/li&gt;
&lt;li&gt;Principle of least privilege, always&lt;/li&gt;
&lt;li&gt;No impact on developer velocity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's when I started looking at Model Context Protocol (MCP) as a security boundary.&lt;/p&gt;




&lt;h2&gt;
  
  
  Understanding MCP as a Security Layer
&lt;/h2&gt;

&lt;p&gt;Before diving into implementation, let's clarify what MCP actually does and why it matters for security.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Model Context Protocol&lt;/strong&gt; is an open standard that sits between your AI agent and your resources. Think of it as a translator and gatekeeper combined.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer → AI Agent → MCP Server → AWS IAM → AWS Resources
                          ↓
                    Security Layer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The MCP server doesn't just pass requests through. It acts as a security boundary that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validates every request before execution&lt;/li&gt;
&lt;li&gt;Translates AI intentions into specific AWS API calls&lt;/li&gt;
&lt;li&gt;Enforces authentication and authorization&lt;/li&gt;
&lt;li&gt;Logs everything for audit trails&lt;/li&gt;
&lt;li&gt;Provides a single point of control&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; Instead of giving AI agents direct AWS credentials, you give them access to an MCP server that has carefully scoped permissions. The AI never touches AWS credentials. It doesn't even know they exist.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Security Architecture
&lt;/h2&gt;

&lt;p&gt;After several iterations, here's the pattern that survived production. I'll explain the thinking behind each layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 1: Authentication Without Permanent Credentials
&lt;/h3&gt;

&lt;p&gt;The first principle: no permanent credentials anywhere in the system.&lt;/p&gt;

&lt;p&gt;Developers authenticate with our existing identity provider (Okta in our case). The identity provider issues a JWT token containing the user's identity and group memberships. The MCP server validates this JWT and issues a short-lived session token - 15 minutes, no exceptions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why 15 minutes?&lt;/strong&gt; Long enough for a debugging session, short enough that a leaked token becomes useless quickly. If someone steals a session token, they have a 15-minute window at most. Compare that to permanent AWS credentials that work forever until manually revoked.&lt;/p&gt;

&lt;p&gt;The MCP server never stores these tokens. They're validated, used, and discarded. When they expire, users re-authenticate. It's a minor inconvenience that prevents major security incidents.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 2: Request Validation
&lt;/h3&gt;

&lt;p&gt;This is where MCP shines as a security boundary. Every request goes through multiple validation checks:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action Allowlist:&lt;/strong&gt; The MCP server maintains a strict list of allowed AWS actions. If the AI requests something not on the list, it's blocked immediately. No wildcards, no "just in case" permissions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pattern Detection:&lt;/strong&gt; I scan every request for dangerous patterns. Words like "delete", "terminate", "destroy" trigger additional scrutiny. Even if the action is technically allowed, suspicious patterns can block the request or require additional approval.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parameter Sanitization:&lt;/strong&gt; Before logging or processing, all sensitive parameters get redacted. Passwords, tokens, API keys - anything that looks like a credential gets replaced with &lt;code&gt;[REDACTED]&lt;/code&gt; in logs. This prevents credential leakage through audit trails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rate Limiting:&lt;/strong&gt; Each user gets a request budget. Exceed it, and requests start getting throttled. This prevents both accidental runaway scripts and intentional abuse.&lt;/p&gt;

&lt;p&gt;The validation happens in milliseconds. Developers don't notice the overhead, but it's the difference between a secure system and a disaster waiting to happen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 3: AWS Execution with Scoped Permissions
&lt;/h3&gt;

&lt;p&gt;The MCP server uses an IAM role with specific permissions. Not admin. Not power user. Just what's needed for legitimate use cases.&lt;/p&gt;

&lt;p&gt;I started by listing every legitimate use case developers had:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read CloudWatch logs for debugging&lt;/li&gt;
&lt;li&gt;List S3 buckets to find data&lt;/li&gt;
&lt;li&gt;Get objects from specific buckets&lt;/li&gt;
&lt;li&gt;Query CloudWatch metrics for dashboards&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then I created IAM policies that allow exactly those actions and nothing else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The key insight:&lt;/strong&gt; Explicit denies for dangerous actions, even if they're not in the allow list. This protects against future policy changes or misconfigurations.&lt;/p&gt;

&lt;p&gt;Example: Even if someone accidentally adds &lt;code&gt;s3:*&lt;/code&gt; to the allow list, an explicit deny on &lt;code&gt;s3:DeleteBucket&lt;/code&gt; still blocks it. Defense in depth.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 4: Comprehensive Audit Trail
&lt;/h3&gt;

&lt;p&gt;CloudTrail logs every AWS API call, but it doesn't capture the context we need. Who made the request? What was the AI prompt? What resources were accessed?&lt;/p&gt;

&lt;p&gt;I built a custom logging layer that captures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User identity (email, not just IAM role)&lt;/li&gt;
&lt;li&gt;Original AI prompt (hashed, not stored in plain text)&lt;/li&gt;
&lt;li&gt;AWS action requested&lt;/li&gt;
&lt;li&gt;Resources accessed&lt;/li&gt;
&lt;li&gt;Result (success/failure)&lt;/li&gt;
&lt;li&gt;Execution time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this goes to CloudWatch Logs in structured JSON format. Now I can query: "Show me all S3 access by user X this week" or "What resources did the AI access when processing this prompt?"&lt;/p&gt;

&lt;p&gt;The logs are immutable and retained for 90 days for compliance.&lt;/p&gt;




&lt;h2&gt;
  
  
  How We Built It
&lt;/h2&gt;

&lt;p&gt;The deployment came down to three critical security decisions. Each one was driven by a specific threat we wanted to prevent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decision 1: Network Isolation Over Convenience
&lt;/h3&gt;

&lt;p&gt;I put the MCP server in a completely separate VPC from production. No shared networks, no VPC peering, nothing. The only communication path is through VPC endpoints to AWS APIs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; If someone compromises the MCP server, they're trapped. No internet access means they can't exfiltrate data. No production VPC access means they can't pivot to other systems. They're stuck in a cage that only opens to specific AWS services.&lt;/p&gt;

&lt;p&gt;I chose ECS Fargate because it gave me this isolation without the overhead of managing EC2 instances. No patching, no scaling configuration, just containers in a locked-down network.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The trade-off:&lt;/strong&gt; More complex networking setup. But the security benefit was worth it. A compromised MCP server becomes useless to an attacker.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decision 2: Explicit Denies as the Last Line of Defense
&lt;/h3&gt;

&lt;p&gt;The IAM policy has two blocks: allows and denies. The allows are specific - exact actions on exact resources. But the denies are what keep me sleeping at night.&lt;/p&gt;

&lt;p&gt;I explicitly deny all delete operations, all terminate operations, all IAM changes, and all KMS key operations. Even if someone misconfigures the allow block and adds &lt;code&gt;s3:*&lt;/code&gt;, the deny on &lt;code&gt;s3:DeleteBucket&lt;/code&gt; still holds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; Policies get changed. People make mistakes. The deny block is the safety net that catches those mistakes before they become incidents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The trade-off:&lt;/strong&gt; More rigid system. If we need to add a delete operation later, we have to modify both blocks. But that friction is intentional - it forces us to think twice about dangerous permissions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Decision 3: Real-Time Alerting Over Post-Incident Analysis
&lt;/h3&gt;

&lt;p&gt;I set up CloudWatch alarms that fire immediately when something looks wrong. High error rates, unusual request volumes, spikes in blocked actions - all trigger alerts to our security team's Slack channel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this matters:&lt;/strong&gt; Logs are great for forensics, but alerts prevent incidents. If the AI starts trying malicious actions, I want to know in real-time, not during next week's log review.&lt;/p&gt;

&lt;p&gt;The alerts are tuned to avoid noise. More than 50 errors in 5 minutes is abnormal. More than 1,000 requests from one user in 5 minutes is suspicious. These thresholds came from watching normal usage patterns for a month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The trade-off:&lt;/strong&gt; Alert fatigue is real. We tune the thresholds monthly based on false positive rates. But I'd rather investigate a false alarm than miss a real attack.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Broke (And How I Fixed It)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Issue 1: Permission Errors Everywhere
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What happened:&lt;/strong&gt; First deployment, every request failed with &lt;code&gt;AccessDenied&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; I was too restrictive. The IAM policy only allowed specific S3 buckets, but developers needed to list buckets first to know what existed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Add &lt;code&gt;s3:ListAllMyBuckets&lt;/code&gt; with a wildcard resource. Let them see what exists, but control what they can read. It's like letting someone see the library catalog without giving them keys to every book.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; Start with read-only list permissions, then restrict data access. Users need to discover resources before they can use them.&lt;/p&gt;




&lt;h3&gt;
  
  
  Issue 2: CloudTrail Logs Were Useless
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What happened:&lt;/strong&gt; CloudTrail showed the MCP server's actions, but not which user requested them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; All requests came from the same IAM role. No way to trace back to individual users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Pass user context through custom CloudWatch Logs. Every MCP request gets logged with the user's email, the action requested, and the resources accessed. Now I can trace every action back to the person who requested it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; CloudTrail alone isn't enough for multi-user systems. You need custom logging to capture user context.&lt;/p&gt;




&lt;h3&gt;
  
  
  Issue 3: AI Agents Tried Creative Exploits
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What happened:&lt;/strong&gt; The AI tried to chain commands to bypass restrictions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example request:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"First list the S3 buckets, then for each bucket, 
download all objects and search for passwords"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; My validation checked individual actions, not sequences. The AI was trying to automate a multi-step attack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Detect and block chaining attempts. Look for words like "then", "after that", "for each", "loop through". Force users to make explicit, separate requests for each action.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; AI agents are creative. They'll try to work around restrictions. You need to think like an attacker.&lt;/p&gt;




&lt;h3&gt;
  
  
  Issue 4: Rate Limiting Was Too Aggressive
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What happened:&lt;/strong&gt; Legitimate users hit rate limits during normal debugging sessions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; I set limits too low (10 requests per minute). Debugging often requires rapid iteration - check logs, adjust query, check again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Tiered rate limits based on action type:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read operations (Get, Describe): 100 requests per 5 minutes&lt;/li&gt;
&lt;li&gt;List operations: 50 requests per 5 minutes&lt;/li&gt;
&lt;li&gt;Write operations: 10 requests per 5 minutes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read operations get higher limits because they're lower risk. Write operations stay restricted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; One-size-fits-all rate limits don't work. Different actions have different risk profiles.&lt;/p&gt;




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

&lt;p&gt;After three months in production, here's what actually matters:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Explicit Denies Are Your Friend
&lt;/h3&gt;

&lt;p&gt;Don't rely on "not allowing" something. Explicitly deny dangerous actions. Even if someone misconfigures the allow rules, the denies hold.&lt;/p&gt;

&lt;p&gt;I have explicit denies for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All delete operations&lt;/li&gt;
&lt;li&gt;All terminate operations&lt;/li&gt;
&lt;li&gt;All IAM operations&lt;/li&gt;
&lt;li&gt;All KMS key operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the "break glass" protections. They prevent catastrophic mistakes.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Log Everything, But Make It Searchable
&lt;/h3&gt;

&lt;p&gt;CloudTrail is great, but you need custom logs for MCP-specific context. I send everything to CloudWatch Logs with structured JSON.&lt;/p&gt;

&lt;p&gt;Now I can query: "Show me all S3 access by user X in the last hour" or "What resources did the AI access when processing this prompt?"&lt;/p&gt;

&lt;p&gt;The logs are immutable and retained for 90 days. If something goes wrong, I can reconstruct exactly what happened.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Sanitize Everything
&lt;/h3&gt;

&lt;p&gt;Never log the actual AI prompts. They might contain sensitive data. I hash them instead.&lt;/p&gt;

&lt;p&gt;You can still correlate requests (same hash = same prompt), but you're not storing potentially sensitive prompts in logs.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Network Isolation Matters
&lt;/h3&gt;

&lt;p&gt;The MCP server runs in a private VPC with no internet access. It can only reach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS API endpoints (via VPC endpoints)&lt;/li&gt;
&lt;li&gt;Internal authentication service&lt;/li&gt;
&lt;li&gt;CloudWatch Logs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If someone compromises the MCP server, they can't exfiltrate data. They're stuck in an isolated network.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Test Your Security Controls
&lt;/h3&gt;

&lt;p&gt;I wrote tests to verify the security controls actually work. Tests like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verify delete operations are blocked&lt;/li&gt;
&lt;li&gt;Verify IAM operations are blocked&lt;/li&gt;
&lt;li&gt;Verify rate limits work&lt;/li&gt;
&lt;li&gt;Verify audit logs capture user context&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run these tests in CI/CD. If they pass, your security controls are working. If they fail, you know immediately.&lt;/p&gt;




&lt;h2&gt;
  
  
  Alternative Approaches I Considered
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option 1: Direct IAM Roles for AI Agents
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simpler architecture&lt;/li&gt;
&lt;li&gt;No MCP server to maintain&lt;/li&gt;
&lt;li&gt;Lower latency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No request validation layer&lt;/li&gt;
&lt;li&gt;Can't block dangerous patterns&lt;/li&gt;
&lt;li&gt;Harder to audit user actions&lt;/li&gt;
&lt;li&gt;AI has direct AWS credentials&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why I didn't use it:&lt;/strong&gt; Too risky. One prompt injection and the AI could delete production resources. The MCP layer provides defense in depth.&lt;/p&gt;




&lt;h3&gt;
  
  
  Option 2: AWS Lambda as MCP Server
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Serverless, no infrastructure&lt;/li&gt;
&lt;li&gt;Automatic scaling&lt;/li&gt;
&lt;li&gt;Pay per request&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cold starts (500ms+)&lt;/li&gt;
&lt;li&gt;15-minute timeout limit&lt;/li&gt;
&lt;li&gt;Harder to maintain state (rate limiting)&lt;/li&gt;
&lt;li&gt;More complex networking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why I didn't use it:&lt;/strong&gt; Cold starts killed the developer experience. Waiting 500ms for every request was frustrating. Fargate has no cold starts.&lt;/p&gt;




&lt;h3&gt;
  
  
  Option 3: API Gateway + Lambda
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built-in rate limiting&lt;/li&gt;
&lt;li&gt;API key management&lt;/li&gt;
&lt;li&gt;Request/response transformation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More complex setup&lt;/li&gt;
&lt;li&gt;Higher cost at scale&lt;/li&gt;
&lt;li&gt;Still has Lambda cold starts&lt;/li&gt;
&lt;li&gt;Overkill for internal use&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why I didn't use it:&lt;/strong&gt; The built-in rate limiting was nice, but not worth the complexity for an internal tool. Fargate + ALB was simpler.&lt;/p&gt;




&lt;h2&gt;
  
  
  Best Practices That Actually Matter
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Start With Read-Only
&lt;/h3&gt;

&lt;p&gt;Deploy with read-only permissions first. Let developers use it for a week. Then gradually add write permissions based on actual needs.&lt;/p&gt;

&lt;p&gt;This prevents over-permissioning. You'll discover what developers actually need, not what they think they need.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Use Separate AWS Accounts
&lt;/h3&gt;

&lt;p&gt;Run the MCP server in a separate AWS account from your production workloads. Use cross-account roles for access.&lt;/p&gt;

&lt;p&gt;If the MCP account is compromised, production is still isolated. It's an extra layer of defense.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Monitor for Anomalies
&lt;/h3&gt;

&lt;p&gt;Set up CloudWatch alarms for unusual patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High error rates (&amp;gt;50 errors in 5 minutes)&lt;/li&gt;
&lt;li&gt;Unusual access patterns (&amp;gt;1,000 requests in 5 minutes)&lt;/li&gt;
&lt;li&gt;Blocked actions (&amp;gt;100 blocks in 5 minutes)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These alerts go to your security team. Response time is critical.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Regular Security Reviews
&lt;/h3&gt;

&lt;p&gt;Every month, review:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which actions are being used most&lt;/li&gt;
&lt;li&gt;Which permissions are never used (remove them)&lt;/li&gt;
&lt;li&gt;Any blocked requests (are they legitimate needs?)&lt;/li&gt;
&lt;li&gt;Rate limit effectiveness&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Security isn't set-and-forget. It requires ongoing attention.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Document Everything
&lt;/h3&gt;

&lt;p&gt;Create a runbook for common scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to add a new allowed action&lt;/li&gt;
&lt;li&gt;How to investigate suspicious activity&lt;/li&gt;
&lt;li&gt;How to rotate credentials&lt;/li&gt;
&lt;li&gt;How to handle a security incident&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When something goes wrong at 2 AM, you'll be glad you documented it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Three months in production taught me that securing AI agent access isn't about perfect security - it's about making attacks harder than they're worth while keeping developers productive.&lt;/p&gt;

&lt;p&gt;The MCP pattern works because it gives you a single point of control. You're not trying to secure the AI agent itself. You're securing the gateway it uses to access your resources. That gateway validates every request, enforces least privilege, logs everything, and runs in an isolated network.&lt;/p&gt;

&lt;p&gt;We went from developers sending production data to ChatGPT to having a secure, auditable system where AI agents help without creating risk. The benefit? No more 2 AM calls about data leaks.&lt;/p&gt;

&lt;p&gt;Is it perfect? No. Can a determined attacker find ways around it? Probably. But it's dramatically better than the alternatives: giving AI agents direct AWS credentials or blocking AI tools entirely and watching developers find workarounds.&lt;/p&gt;

&lt;p&gt;The key insight: Security is not about building walls. It's about building gates with guards. MCP is that gate.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Security is not about building walls. It's about building gates with guards."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  📌 Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Thank you for reading! I hope this gave you practical ideas for securing AI agent access in your environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Found this useful?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❤️ Like if it helped you think through your security approach&lt;/li&gt;
&lt;li&gt;🦄 Unicorn if you're implementing this pattern&lt;/li&gt;
&lt;li&gt;💾 Save for your next security review&lt;/li&gt;
&lt;li&gt;🔄 Share with your security team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Follow me for more on:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS security patterns&lt;/li&gt;
&lt;li&gt;AI/ML infrastructure&lt;/li&gt;
&lt;li&gt;Cloud architecture&lt;/li&gt;
&lt;li&gt;DevSecOps practices&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💡 What's Next
&lt;/h2&gt;

&lt;p&gt;I'm working on a follow-up article about monitoring and alerting for MCP deployments. Follow for updates.&lt;/p&gt;

&lt;p&gt;Also exploring: Multi-region MCP deployments and disaster recovery patterns.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌐 Portfolio &amp;amp; Work
&lt;/h2&gt;

&lt;p&gt;Explore my full body of work, certifications, and architecture projects:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;Visit My Website&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Services I Offer
&lt;/h2&gt;

&lt;p&gt;Looking for hands-on guidance with cloud security or AI infrastructure?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud Security Architecture (AWS / Azure)&lt;/li&gt;
&lt;li&gt;AI/ML Infrastructure Design&lt;/li&gt;
&lt;li&gt;Security Audit &amp;amp; Remediation&lt;/li&gt;
&lt;li&gt;Technical Writing &amp;amp; Documentation&lt;/li&gt;
&lt;li&gt;Architecture Reviews&lt;/li&gt;
&lt;li&gt;1:1 Technical Mentorship&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🤝 Let's Connect
&lt;/h2&gt;

&lt;p&gt;Questions about implementing this pattern? Drop a comment or connect with me on &lt;a href="https://www.linkedin.com/in/sarvar04/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For consulting or technical discussions: &lt;a href="mailto:simplynadaf@gmail.com"&gt;simplynadaf@gmail.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stay secure!&lt;/strong&gt; 🔒&lt;/p&gt;

</description>
      <category>aws</category>
      <category>ai</category>
      <category>agents</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Amazon S3 Files: The Game Changer We've Been Waiting For</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Tue, 14 Apr 2026 14:09:19 +0000</pubDate>
      <link>https://dev.to/aws-builders/amazon-s3-files-the-game-changer-weve-been-waiting-for-2515</link>
      <guid>https://dev.to/aws-builders/amazon-s3-files-the-game-changer-weve-been-waiting-for-2515</guid>
      <description>&lt;p&gt;👋 Hey there, tech enthusiasts! &lt;/p&gt;

&lt;p&gt;I'm Sarvar, a Cloud Architect with a passion for transforming complex technological challenges into elegant solutions. With extensive experience spanning Cloud Operations (AWS &amp;amp; Azure), Data Operations, Analytics, DevOps, and Generative AI, I've had the privilege of architecting solutions for global enterprises that drive real business impact. Through this article series, I'm excited to share practical insights, best practices, and hands-on experiences from my journey in the tech world. Whether you're a seasoned professional or just starting out, I aim to break down complex concepts into digestible pieces that you can apply in your projects.&lt;/p&gt;

&lt;p&gt;Let's dive in and explore the fascinating world of cloud technology together! 🚀&lt;/p&gt;




&lt;p&gt;After working with AWS for over a decade, I've spent countless hours explaining to clients why they can't just "edit a file in S3" the way they would on their local computer. I've drawn diagrams, created analogies, and watched developers struggle with the limitations of object storage versus file systems. Well, that conversation just got a lot more interesting.&lt;/p&gt;

&lt;p&gt;In late 2025, AWS released Amazon S3 Files, and honestly, it's one of those features that makes you wonder why it took so long. Let me walk you through what it is, why it matters, and when you should (and shouldn't) use it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; S3 Files is a true POSIX-compliant file system for S3 buckets. Unlike S3 FUSE/Mountpoint, it provides full file system semantics with sub-millisecond latency. Best for: ML training, AI agents, data analytics, shared development environments.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem S3 Files Solves
&lt;/h2&gt;

&lt;p&gt;Let me start with a story that probably sounds familiar. Last year, I worked with a machine learning team training large language models. Their workflow looked like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Store training datasets in S3 (hundreds of gigabytes of text data)&lt;/li&gt;
&lt;li&gt;Copy datasets to an EBS volume attached to their GPU instances&lt;/li&gt;
&lt;li&gt;Train the model, saving checkpoints every hour&lt;/li&gt;
&lt;li&gt;Copy checkpoints back to S3 for durability&lt;/li&gt;
&lt;li&gt;When spot instances get interrupted, restart everything&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This workflow had several problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data duplication&lt;/strong&gt;: Paying for storage twice (S3 + EBS on every instance)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time waste&lt;/strong&gt;: 30-45 minutes copying data before training could start&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complexity&lt;/strong&gt;: Custom scripts to sync checkpoints and handle interruptions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost&lt;/strong&gt;: Running 2TB EBS volumes on multiple GPU instances 24/7&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spot instance pain&lt;/strong&gt;: Every interruption meant re-copying everything&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They tried using S3 FUSE and Mountpoint S3 to mount their S3 bucket directly, but ran into a wall. Imagine training a model for 6 hours, then having the checkpoint save fail because the training framework needs to update the checkpoint metadata a basic file operation that S3 FUSE simply can't handle. The training framework would write 95% of the checkpoint file, then fail when it tried to seek back to update the header. Why? Because S3 FUSE isn't a real file system it's just an API wrapper pretending to be one.&lt;/p&gt;

&lt;p&gt;This is exactly the problem S3 Files solves.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Exactly Is S3 Files?
&lt;/h2&gt;

&lt;p&gt;Amazon S3 Files is a true, POSIX-compliant file system that sits on top of your S3 buckets. Think of it as a high-performance bridge between your compute resources (EC2, Lambda, ECS, EKS) and your S3 data a smart caching layer that speaks both "file system" and "S3 object" fluently.&lt;/p&gt;

&lt;p&gt;Here's what makes it different from previous solutions:&lt;/p&gt;

&lt;h3&gt;
  
  
  S3 Files vs. The Old Ways
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;S3 FUSE / Mountpoint S3:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API wrappers that translate file operations into S3 API calls&lt;/li&gt;
&lt;li&gt;Limited file system semantics&lt;/li&gt;
&lt;li&gt;Can't handle operations like seeking backward in files&lt;/li&gt;
&lt;li&gt;No true concurrent write support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;S3 Files:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built on Amazon Elastic File System (EFS) technology&lt;/li&gt;
&lt;li&gt;True POSIX compliance (supports all NFS v4.1+ operations)&lt;/li&gt;
&lt;li&gt;Sub-millisecond latency for cached data&lt;/li&gt;
&lt;li&gt;Full file system semantics (create, read, update, delete, seek, append)&lt;/li&gt;
&lt;li&gt;Concurrent access from multiple compute resources&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How S3 Files Actually Works
&lt;/h2&gt;

&lt;p&gt;The architecture is clever. When you create an S3 file system and mount it to your EC2 instance, here's what happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Initial Mount&lt;/strong&gt;: You see your S3 bucket as a directory structure. No data is copied yet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lazy Loading&lt;/strong&gt;: When you access a file, only that file's metadata and content are loaded into a high-performance cache layer (built on EFS).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Smart Caching&lt;/strong&gt;: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frequently accessed files stay in the cache (sub-millisecond access)&lt;/li&gt;
&lt;li&gt;Large sequential reads go directly from S3 (maximizing throughput)&lt;/li&gt;
&lt;li&gt;Only requested byte ranges are transferred (minimizing costs)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Write Operations&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writes go to the cache first (fast)&lt;/li&gt;
&lt;li&gt;Changes sync back to S3 automatically within minutes&lt;/li&gt;
&lt;li&gt;You get immediate file system consistency&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Bidirectional Sync&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Changes in the file system appear in S3 within minutes&lt;/li&gt;
&lt;li&gt;Changes in S3 appear in the file system within seconds&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Real-World Example: AI Model Training Pipeline
&lt;/h2&gt;

&lt;p&gt;Let me show you a practical example. I recently helped an AI research team migrate their model training pipeline to use S3 Files.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Scenario
&lt;/h3&gt;

&lt;p&gt;They train large language models with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;500GB training datasets (tokenized text)&lt;/li&gt;
&lt;li&gt;Hourly checkpoint saves (10-50GB each)&lt;/li&gt;
&lt;li&gt;Multi-GPU distributed training&lt;/li&gt;
&lt;li&gt;Spot instances for cost optimization&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Old Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;S3 Dataset → Copy to EBS → Train Model → Save Checkpoint to EBS → Sync to S3 → Spot Interruption → Repeat
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;30-45 minutes copying data before training starts&lt;/li&gt;
&lt;li&gt;$1,200/month in EBS costs across GPU instances&lt;/li&gt;
&lt;li&gt;Complex checkpoint sync logic&lt;/li&gt;
&lt;li&gt;Spot interruptions meant starting over with data copies&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The New Architecture with S3 Files
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;S3 Dataset → Mount S3 Files → Train Model → Checkpoints auto-sync to S3 → Spot Interruption → Remount &amp;amp; Resume
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Results
&lt;/h3&gt;

&lt;p&gt;With S3 Files, they mount their S3 bucket directly to their GPU instances. Training frameworks like PyTorch can now write checkpoints with full file system semantics updating headers, seeking to specific positions, and appending data operations that previously failed with S3 FUSE.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Training startup&lt;/strong&gt;: Reduced from 45 minutes to 2 minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost savings&lt;/strong&gt;: $1,200/month in EBS costs eliminated&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spot instance recovery&lt;/strong&gt;: From 45 minutes to 5 minutes (just remount and resume)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplified code&lt;/strong&gt;: Removed 300+ lines of checkpoint sync and recovery logic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better reliability&lt;/strong&gt;: No more corrupted checkpoints or failed saves&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Use Cases Where S3 Files Shines
&lt;/h2&gt;

&lt;p&gt;Now that you understand how it works, let's look at where S3 Files makes the biggest impact.&lt;/p&gt;

&lt;p&gt;Based on my experience and conversations with other architects, here are the scenarios where S3 Files is a perfect fit:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Machine Learning Training
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario&lt;/strong&gt;: Training models on large datasets stored in S3&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before S3 Files&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Copy entire dataset to EBS/EFS before training&lt;/li&gt;
&lt;li&gt;Wait hours for data transfer&lt;/li&gt;
&lt;li&gt;Pay for duplicate storage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;With S3 Files&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mount S3 bucket directly&lt;/li&gt;
&lt;li&gt;Training starts immediately (lazy loading)&lt;/li&gt;
&lt;li&gt;Only accessed data is cached&lt;/li&gt;
&lt;li&gt;Multiple training instances can share the same data&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. AI Agents and Automation
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario&lt;/strong&gt;: AI agents using Python libraries and CLI tools that expect file systems&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Challenge&lt;/strong&gt;: Many AI agents and automation tools are built with traditional file system operations in mind. Rewriting them to use S3 APIs directly would require significant code changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With S3 Files&lt;/strong&gt;: Your existing code that expects file system access works without modification. The agent can read, write, and process files as if they were on a local file system, while S3 Files handles the synchronization with your S3 bucket automatically.&lt;/p&gt;

&lt;p&gt;This means AI agents can use standard Python libraries, shell scripts, and CLI tools without any code changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Shared Development Environments
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario&lt;/strong&gt;: Multiple developers or containers need concurrent access to shared data&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With S3 Files&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mount the same S3 bucket on multiple EC2 instances&lt;/li&gt;
&lt;li&gt;Developers can read and write simultaneously&lt;/li&gt;
&lt;li&gt;Changes are visible across all instances within seconds&lt;/li&gt;
&lt;li&gt;No complex locking mechanisms needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is especially powerful for Kubernetes clusters where pods need shared access to training data or model artifacts across nodes.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Content Management Systems
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario&lt;/strong&gt;: CMS storing media files that need to be accessed by multiple web servers&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With S3 Files&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All web servers mount the same S3 bucket&lt;/li&gt;
&lt;li&gt;Upload once, available everywhere immediately&lt;/li&gt;
&lt;li&gt;No need for S3 sync scripts or CDN invalidation delays&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Important Considerations
&lt;/h2&gt;

&lt;p&gt;Let me share some lessons learned from real implementations:&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance Characteristics
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;First access&lt;/strong&gt;: Slower (loading from S3 to cache)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subsequent access&lt;/strong&gt;: Sub-millisecond (from cache)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Large sequential reads&lt;/strong&gt;: Served directly from S3 (high throughput)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Small random reads&lt;/strong&gt;: Served from cache (low latency)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pro tip&lt;/strong&gt;: If you know which files you'll need, you can pre-load them into the cache to avoid first-access latency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Consistency Model
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;File system to S3&lt;/strong&gt;: Changes appear in S3 within minutes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3 to file system&lt;/strong&gt;: Changes appear in file system within seconds (sometimes up to a minute)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: If you need immediate consistency, use the file system as your source of truth during processing, then let it sync to S3.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cost Structure
&lt;/h3&gt;

&lt;p&gt;S3 Files has three cost components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Storage&lt;/strong&gt;: $0.30/GB-month for data in the cache&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data access&lt;/strong&gt;: 

&lt;ul&gt;
&lt;li&gt;$0.03/GB for reads&lt;/li&gt;
&lt;li&gt;$0.06/GB for writes&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3 costs&lt;/strong&gt;: Standard S3 storage and request costs&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Cost optimization tip&lt;/strong&gt;: The cache only stores actively used data. If you're processing 100GB from a 10TB bucket, you only pay cache costs for the 100GB.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Encryption in transit&lt;/strong&gt;: TLS 1.3 (automatic)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encryption at rest&lt;/strong&gt;: SSE-S3 or KMS (your choice)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access control&lt;/strong&gt;: IAM policies + POSIX permissions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network isolation&lt;/strong&gt;: Mount targets live in your VPC&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  When NOT to Use S3 Files
&lt;/h2&gt;

&lt;p&gt;Being honest about limitations is important. S3 Files isn't the right choice for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Simple object storage&lt;/strong&gt;: If you're just storing and retrieving whole objects, regular S3 is simpler and cheaper.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Infrequent access&lt;/strong&gt;: If you rarely access your data, the cache storage costs aren't worth it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Windows-specific features&lt;/strong&gt;: If you need Windows file system features, use FSx for Windows File Server.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Extreme performance HPC&lt;/strong&gt;: If you need the absolute highest performance for HPC workloads, FSx for Lustre is better optimized.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;On-premises integration&lt;/strong&gt;: If you're migrating from on-prem NAS, FSx for NetApp ONTAP provides better compatibility.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;S3 Files is available in all commercial AWS regions as of April 2026. You can create an S3 file system through the AWS Console, AWS CLI, or infrastructure as code tools like CloudFormation and Terraform.&lt;/p&gt;

&lt;p&gt;The basic workflow involves:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creating an S3 file system linked to your bucket&lt;/li&gt;
&lt;li&gt;Configuring network access (VPC, subnets, security groups)&lt;/li&gt;
&lt;li&gt;Setting up IAM permissions&lt;/li&gt;
&lt;li&gt;Mounting the file system on your compute resources&lt;/li&gt;
&lt;li&gt;Using standard file operations to interact with your S3 data&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;In the next article, I'll walk you through a complete hands-on implementation with step-by-step commands and real examples.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  My Take After Using It
&lt;/h2&gt;

&lt;p&gt;I've been working with S3 Files since early access in late 2025, across several client projects. Here's my honest assessment:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I love:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It eliminates entire categories of data movement code&lt;/li&gt;
&lt;li&gt;Applications that expect file systems just work&lt;/li&gt;
&lt;li&gt;The lazy loading is genuinely smart&lt;/li&gt;
&lt;li&gt;Cost savings are real when you eliminate data duplication&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What to watch out for:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The sync delay (minutes) can surprise people expecting instant S3 updates&lt;/li&gt;
&lt;li&gt;You need to understand the caching behavior to optimize costs&lt;/li&gt;
&lt;li&gt;Security group configuration is an extra step that trips people up&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Bottom line&lt;/strong&gt;: If you're currently copying data between S3 and file systems, or if you're struggling with S3 FUSE limitations, S3 Files is absolutely worth evaluating. It's not a silver bullet, but for the right use cases, it's transformative.&lt;/p&gt;




&lt;h2&gt;
  
  
  Availability and Pricing
&lt;/h2&gt;

&lt;p&gt;As of April 2026, S3 Files is available in all commercial AWS regions.&lt;/p&gt;

&lt;p&gt;Pricing varies by region, but in us-east-1:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cache storage: $0.30/GB-month&lt;/li&gt;
&lt;li&gt;Read operations: $0.03/GB&lt;/li&gt;
&lt;li&gt;Write operations: $0.06/GB&lt;/li&gt;
&lt;li&gt;Plus standard S3 costs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check the &lt;a href="https://aws.amazon.com/s3/pricing/" rel="noopener noreferrer"&gt;S3 pricing page&lt;/a&gt; for your region's specific pricing.&lt;/p&gt;




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

&lt;p&gt;Amazon S3 Files represents a significant shift in how we can architect cloud applications. For years, we've had to choose between S3's durability and cost-effectiveness versus a file system's interactive capabilities. S3 Files eliminates that tradeoff.&lt;/p&gt;

&lt;p&gt;Whether you're training ML models on spot instances, building AI agents, running data analytics pipelines, or any workload that needs shared, interactive file access to S3 data, S3 Files deserves a serious look. It's not just a new feature it's a new way of thinking about data access in the cloud.&lt;/p&gt;

&lt;p&gt;The best part? You don't need to rewrite your applications. If your code works with files, it'll work with S3 Files. And that's the kind of simplicity we all need more of in cloud architecture.&lt;/p&gt;

&lt;p&gt;In my next article, I'll show you exactly how to set up S3 Files with a complete hands-on implementation guide, including all the commands, configurations, and real-world testing scenarios.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have you tried S3 Files yet? I'd love to hear about your use cases and experiences. The cloud architecture community learns best when we share real-world implementations, not just theory.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📌 Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Thank you for reading! I hope this article gave you practical insights and a clearer perspective on the topic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Was this helpful?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❤️ Like if it added value&lt;/li&gt;
&lt;li&gt;🦄 Unicorn if you’re applying it today&lt;/li&gt;
&lt;li&gt;💾 Save for your next optimization session&lt;/li&gt;
&lt;li&gt;🔄 Share with your team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Follow me for more on:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS architecture patterns&lt;/li&gt;
&lt;li&gt;FinOps automation&lt;/li&gt;
&lt;li&gt;Multi-account strategies&lt;/li&gt;
&lt;li&gt;AI-driven DevOps&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💡 What’s Next
&lt;/h2&gt;

&lt;p&gt;More deep dives coming soon on cloud operations, GenAI, Agentic-AI, DevOps, and data workflows follow for weekly insights.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌐 Portfolio &amp;amp; Work
&lt;/h2&gt;

&lt;p&gt;You can explore my full body of work, certifications, architecture projects, and technical articles here:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;Visit My Website&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Services I Offer
&lt;/h2&gt;

&lt;p&gt;If you're looking for hands-on guidance or collaboration, I provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud Architecture Consulting (AWS / Azure)&lt;/li&gt;
&lt;li&gt;DevSecOps &amp;amp; Automation Design&lt;/li&gt;
&lt;li&gt;FinOps Optimization Reviews&lt;/li&gt;
&lt;li&gt;Technical Writing (Cloud, DevOps, GenAI)&lt;/li&gt;
&lt;li&gt;Product &amp;amp; Architecture Reviews&lt;/li&gt;
&lt;li&gt;Mentorship &amp;amp; 1:1 Technical Guidance&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🤝 Let’s Connect
&lt;/h2&gt;

&lt;p&gt;I’d love to hear your thoughts drop a comment or connect with me on &lt;a href="https://www.linkedin.com/in/sarvar04/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For collaborations, consulting, or technical discussions, feel free to reach out directly at &lt;a href="mailto:simplynadaf@gmail.com"&gt;simplynadaf@gmail.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Happy Learning&lt;/strong&gt; 🚀&lt;/p&gt;

</description>
      <category>aws</category>
      <category>ai</category>
      <category>cloud</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Building Your First VPC: AWS Networking with Terraform</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Wed, 08 Apr 2026 10:04:44 +0000</pubDate>
      <link>https://dev.to/aws-builders/building-your-first-vpc-aws-networking-with-terraform-3h34</link>
      <guid>https://dev.to/aws-builders/building-your-first-vpc-aws-networking-with-terraform-3h34</guid>
      <description>&lt;p&gt;👋 Hey there, tech enthusiasts! &lt;/p&gt;

&lt;p&gt;I'm Sarvar, a Cloud Architect with a passion for transforming complex technological challenges into elegant solutions. With extensive experience spanning Cloud Operations (AWS &amp;amp; Azure), Data Operations, Analytics, DevOps, and Generative AI, I've had the privilege of architecting solutions for global enterprises that drive real business impact. Through this article series, I'm excited to share practical insights, best practices, and hands-on experiences from my journey in the tech world. Whether you're a seasoned professional or just starting out, I aim to break down complex concepts into digestible pieces that you can apply in your projects.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Welcome Back!
&lt;/h2&gt;

&lt;p&gt;Remember in &lt;a href="https://dev.to/aws-builders/terraform-variables-and-outputs-making-your-infrastructure-flexible-3ebc"&gt;Article 5&lt;/a&gt; when you learned about variables and outputs? You created flexible, reusable S3 bucket configurations. That's great for storage, but what about &lt;strong&gt;networking&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Here's the reality:&lt;/strong&gt; S3 buckets are public services. But what about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EC2 instances that need network isolation?&lt;/li&gt;
&lt;li&gt;Databases that should never be directly accessible from the internet?&lt;/li&gt;
&lt;li&gt;Applications that need both public and private components?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;That's where VPC (Virtual Private Cloud) comes in.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;By the end of this article, you'll:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Understand VPC fundamentals (CIDR, subnets, routing)&lt;/li&gt;
&lt;li&gt;✅ Create a production-ready VPC with Terraform&lt;/li&gt;
&lt;li&gt;✅ Configure public and private subnets&lt;/li&gt;
&lt;li&gt;✅ Set up Internet Gateway and NAT Gateway&lt;/li&gt;
&lt;li&gt;✅ Master route tables and associations&lt;/li&gt;
&lt;li&gt;✅ Use data sources to fetch AWS information&lt;/li&gt;
&lt;li&gt;✅ Build cost-optimized VPC architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Time Required:&lt;/strong&gt; 45 minutes (20 min read + 25 min practice)&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Cost:&lt;/strong&gt; ~$0.05 for testing (destroy immediately!)&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Difficulty:&lt;/strong&gt; Intermediate&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚠️ IMPORTANT:&lt;/strong&gt; NAT Gateway costs $0.045/hour (~$32/month). We'll create it for learning, but &lt;strong&gt;destroy it immediately&lt;/strong&gt; after testing!&lt;/p&gt;

&lt;p&gt;Let's build real infrastructure! 🚀&lt;/p&gt;


&lt;h2&gt;
  
  
  💔 The Problem: Networking Confusion
&lt;/h2&gt;
&lt;h3&gt;
  
  
  The Manual Nightmare
&lt;/h3&gt;

&lt;p&gt;Imagine this scenario (maybe you've lived it):&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your Task:&lt;/strong&gt; "Set up a VPC for our new application"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You open AWS Console:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Step 1: Create VPC... wait, what's a CIDR block?
Step 2: Create subnets... public or private? What's the difference?
Step 3: Internet Gateway... where does this go?
Step 4: NAT Gateway... why do I need this?
Step 5: Route tables... which subnet goes where?
Step 6: *2 hours later* ... did I configure this correctly?
Step 7: *Application doesn't work* ... something's wrong with routing
Step 8: *Starts over* 😰
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Common Problems:
&lt;/h3&gt;

&lt;p&gt;❌ &lt;strong&gt;Forgot route table associations&lt;/strong&gt; - Subnets can't reach internet&lt;br&gt;&lt;br&gt;
❌ &lt;strong&gt;Wrong CIDR blocks&lt;/strong&gt; - IP addresses overlap&lt;br&gt;&lt;br&gt;
❌ &lt;strong&gt;NAT Gateway in wrong subnet&lt;/strong&gt; - Private instances can't update&lt;br&gt;&lt;br&gt;
❌ &lt;strong&gt;Inconsistent configuration&lt;/strong&gt; - Resources don't work together&lt;br&gt;&lt;br&gt;
❌ &lt;strong&gt;Can't remember configuration&lt;/strong&gt; - No documentation&lt;br&gt;&lt;br&gt;
❌ &lt;strong&gt;Takes hours to set up&lt;/strong&gt; - Clicking through endless screens  &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sound familiar?&lt;/strong&gt; Let's fix this with Terraform.&lt;/p&gt;


&lt;h2&gt;
  
  
  🌐 What is a VPC? (Quick Theory)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Simple Definition
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;VPC (Virtual Private Cloud)&lt;/strong&gt; is your own private network in AWS. Think of it like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Traditional Data Center:&lt;/strong&gt; Physical building with servers, switches, routers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;VPC:&lt;/strong&gt; Virtual data center with virtual servers, virtual switches, virtual routers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Point:&lt;/strong&gt; Your VPC is &lt;strong&gt;isolated&lt;/strong&gt; from other AWS accounts. It's YOUR private network.&lt;/p&gt;
&lt;h3&gt;
  
  
  VPC Components (The Building Blocks)
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VPC (Your Private Network)
├── CIDR Block (IP Address Range)
├── Subnets (Subdivisions of your network)
│   ├── Public Subnets (Internet-accessible)
│   └── Private Subnets (Internal only)
├── Internet Gateway (Door to the internet)
├── NAT Gateway (Private subnet's internet access)
└── Route Tables (GPS for network traffic)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Understanding CIDR Blocks
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;CIDR&lt;/strong&gt; = Classless Inter-Domain Routing (fancy name for IP address ranges)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Examples:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;10.0.0.0/16&lt;/code&gt; = 65,536 IP addresses (10.0.0.0 to 10.0.255.255)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;10.0.1.0/24&lt;/code&gt; = 256 IP addresses (10.0.1.0 to 10.0.1.255)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;10.0.2.0/24&lt;/code&gt; = 256 IP addresses (10.0.2.0 to 10.0.2.255)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Simple Rule:&lt;/strong&gt; Smaller number after &lt;code&gt;/&lt;/code&gt; = More IP addresses&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/16&lt;/code&gt; = 65,536 IPs (big network)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/24&lt;/code&gt; = 256 IPs (small network)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/32&lt;/code&gt; = 1 IP (single host)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Don't worry!&lt;/strong&gt; You don't need to be a networking expert. Just follow the patterns.&lt;/p&gt;
&lt;h3&gt;
  
  
  Public vs Private Subnets
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Public Subnet:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Has route to Internet Gateway&lt;/li&gt;
&lt;li&gt;✅ Resources get public IP addresses&lt;/li&gt;
&lt;li&gt;✅ Directly accessible from internet&lt;/li&gt;
&lt;li&gt;📍 Use for: Web servers, load balancers, bastion hosts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Private Subnet:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Has route to NAT Gateway (not Internet Gateway)&lt;/li&gt;
&lt;li&gt;❌ No public IP addresses&lt;/li&gt;
&lt;li&gt;❌ Not directly accessible from internet&lt;/li&gt;
&lt;li&gt;📍 Use for: Databases, application servers, internal services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why both?&lt;/strong&gt; Security! Keep sensitive resources (databases) in private subnets.&lt;/p&gt;


&lt;h2&gt;
  
  
  🏗️ What We'll Build Today
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Architecture Overview
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VPC: 10.0.0.0/16 (65,536 IPs)
│
├── Public Subnet 1: 10.0.1.0/24 (256 IPs) - us-east-1a
│   ├── Internet Gateway attached
│   └── Route: 0.0.0.0/0 → Internet Gateway
│
├── Private Subnet 1: 10.0.11.0/24 (256 IPs) - us-east-1a
│   └── Route: 0.0.0.0/0 → NAT Gateway
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Why this design?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Single-AZ&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Public subnets&lt;/strong&gt; = For load balancers, web servers&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Private subnets&lt;/strong&gt; = For databases, application servers&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;NAT Gateway&lt;/strong&gt; = Private subnets can download updates&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Production-ready&lt;/strong&gt; = This is how real companies do it&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;The complete source code and Terraform configuration used in this article can be found on GitHub:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/simplynadaf" rel="noopener noreferrer"&gt;
        simplynadaf
      &lt;/a&gt; / &lt;a href="https://github.com/simplynadaf/terraform-by-sarvar" rel="noopener noreferrer"&gt;
        terraform-by-sarvar
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Terraform Series
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🚀 Terraform By Sarvar&lt;/h1&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Complete Terraform tutorial series from basics to advanced concepts&lt;/h3&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://www.terraform.io/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/446abdaa5e617237549619105d10cbd99f693a057dc7b366edd5256283015a75/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5465727261666f726d2d76312e31342b2d3632334345343f7374796c653d666f722d7468652d6261646765266c6f676f3d7465727261666f726d266c6f676f436f6c6f723d7768697465" alt="Terraform"&gt;&lt;/a&gt;
&lt;a href="https://aws.amazon.com/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/0b14c31424193ad1651b7422c0c3fd0f5339f3f2632f5ecc3d5313c83e8aeba0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4157532d436c6f75642d4646393930303f7374796c653d666f722d7468652d6261646765266c6f676f3d616d617a6f6e2d617773266c6f676f436f6c6f723d7768697465" alt="AWS"&gt;&lt;/a&gt;
&lt;a href="https://github.com/simplynadaf/terraform-by-sarvar/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/9218332452902d9e542a100d0af126fd3174a116456614d2cf093546a13783db/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d677265656e2e7376673f7374796c653d666f722d7468652d6261646765" alt="License"&gt;&lt;/a&gt;
&lt;a href="https://dev.to/sarvar_04/series/36958" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/ad1f81014cac91cbb305e20c6fb83301ac4992821ede633a6a83bda4379ed118/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6465762e746f2d5365726965732d3041304130413f7374796c653d666f722d7468652d6261646765266c6f676f3d6465762e746f266c6f676f436f6c6f723d7768697465" alt="dev.to"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://dev.to/sarvar_04/series/36963" rel="nofollow"&gt;📖 Read the Series&lt;/a&gt; • &lt;a href="https://sarvarnadaf.com" rel="nofollow noopener noreferrer"&gt;🌐 Portfolio&lt;/a&gt; • &lt;a href="https://www.linkedin.com/in/sarvar04/" rel="nofollow noopener noreferrer"&gt;💼 LinkedIn&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📚 Series Overview&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;This repository contains &lt;strong&gt;ONLY Terraform code examples&lt;/strong&gt; for the &lt;strong&gt;Terraform By Sarvar&lt;/strong&gt; tutorial series.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ IMPORTANT:&lt;/strong&gt; This repo contains only &lt;code&gt;.tf&lt;/code&gt; files and infrastructure code. Articles are published on dev.to, not stored here.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;📖 &lt;strong&gt;Read the series on dev.to:&lt;/strong&gt; &lt;a href="https://dev.to/sarvar_04/series/36963" rel="nofollow"&gt;https://dev.to/sarvar_04/series/36963&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;🎯 What You'll Learn&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;✅ Infrastructure as Code (IaC) fundamentals&lt;/li&gt;
&lt;li&gt;✅ Terraform basics to advanced concepts&lt;/li&gt;
&lt;li&gt;✅ AWS resource provisioning&lt;/li&gt;
&lt;li&gt;✅ Best practices and real-world patterns&lt;/li&gt;
&lt;li&gt;✅ Production-ready configurations&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;📊 Series Progress&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/5d15589585043bcdb6defccdfdd8103a9e0c81e1d32502a3aee5acca8cfe3462/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f41727469636c65732d3725324631302d626c75653f7374796c653d666c61742d737175617265"&gt;&lt;img src="https://camo.githubusercontent.com/5d15589585043bcdb6defccdfdd8103a9e0c81e1d32502a3aee5acca8cfe3462/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f41727469636c65732d3725324631302d626c75653f7374796c653d666c61742d737175617265" alt="Progress"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/ab5bccc65fd0dfc759cb04ffe3446775f8674a09e936f473d7a51abcd3e02713/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5374617475732d4163746976652d737563636573733f7374796c653d666c61742d737175617265"&gt;&lt;img src="https://camo.githubusercontent.com/ab5bccc65fd0dfc759cb04ffe3446775f8674a09e936f473d7a51abcd3e02713/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5374617475732d4163746976652d737563636573733f7374796c653d666c61742d737175617265" alt="Status"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📖 Article Series&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;📘 Foundation (Articles 1-5) ✅ Complete&lt;/h3&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aws-builders/why-every-developer-should-learn-terraform-in-2026-and-how-to-start--4fk0" rel="nofollow"&gt;Introduction to Terraform &amp;amp; IaC&lt;/a&gt; - ✅ Published&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aws-builders/installing-terraform-and-setting-up-your-environment-1j9b" rel="nofollow"&gt;Installation &amp;amp; Setup&lt;/a&gt; - ✅ Published&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aws-builders/your-first-infrastructure-as-code-in-four-commands-46ep" rel="nofollow"&gt;Your First AWS Resource&lt;/a&gt; - ✅ Published&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aws-builders/terraform-state-the-one-file-you-cant-afford-to-lose-33l4" rel="nofollow"&gt;Understanding Terraform State&lt;/a&gt; - ✅ Published&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aws-builders/terraform-variables-and-outputs-making-your-infrastructure-flexible-3ebc" rel="nofollow"&gt;Variables and Outputs&lt;/a&gt; - ✅ Published&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;📗 Real Infrastructure (Articles 6-10)&lt;/h3&gt;

&lt;/div&gt;
&lt;ol start="6"&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aws-builders/building-your-first-vpc-aws-networking-with-terraform-3h34" rel="nofollow"&gt;Building a VPC from Scratch&lt;/a&gt;…&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/simplynadaf/terraform-by-sarvar" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;





&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;p&gt;Follow these steps to run the project on your local machine:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clone the repository:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/simplynadaf/terraform-by-sarvar.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Navigate to the project directory:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;terraform-by-sarvar/articles/08-lambda-serverless
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📝 Step-by-Step: Building the VPC
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Create Project Directory
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/terraform-vpc-demo
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/terraform-vpc-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fgpa2nbdea2ajusf04zow.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%2Fgpa2nbdea2ajusf04zow.png" alt=" " width="729" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Create variables.tf
&lt;/h3&gt;

&lt;p&gt;Let's start with variables for flexibility:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano variables.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Copy this code:&lt;/strong&gt;&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="c1"&gt;# AWS Region&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"aws_region"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS region where resources will be created"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&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="c1"&gt;# Project Name&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"project_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Project name to be used in resource naming"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-vpc"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Environment&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"environment"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Environment name (dev, staging, prod)"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# VPC CIDR Block&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"vpc_cidr"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"CIDR block for VPC"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Public Subnet CIDR&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"public_subnet_cidr"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"CIDR block for public subnet"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.1.0/24"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Private Subnet CIDR&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"private_subnet_cidr"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"CIDR block for private subnet"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.11.0/24"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fyod6syor5q2fe77r1t7r.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%2Fyod6syor5q2fe77r1t7r.png" alt=" " width="559" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's happening here?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All CIDR blocks are variables (easy to change)&lt;/li&gt;
&lt;li&gt;Project name and environment for tagging&lt;/li&gt;
&lt;li&gt;Default values provided (but can be overridden)&lt;/li&gt;
&lt;li&gt;Cost-optimized: 1 public + 1 private subnet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Save the file&lt;/strong&gt; (Ctrl+X, then Y, then Enter)&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Create terraform.tfvars
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Copy this code:&lt;/strong&gt;&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="c1"&gt;# Default configuration for development environment&lt;/span&gt;
&lt;span class="nx"&gt;aws_region&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;span class="nx"&gt;project_name&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-vpc"&lt;/span&gt;
&lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt;

&lt;span class="c1"&gt;# VPC Configuration&lt;/span&gt;
&lt;span class="nx"&gt;vpc_cidr&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&lt;/span&gt;

&lt;span class="c1"&gt;# Subnets&lt;/span&gt;
&lt;span class="nx"&gt;public_subnet_cidr&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.1.0/24"&lt;/span&gt;
&lt;span class="nx"&gt;private_subnet_cidr&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.11.0/24"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Save the file&lt;/strong&gt;&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%2Fe3pbagzz56c4mhxm6o14.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%2Fe3pbagzz56c4mhxm6o14.png" alt=" " width="800" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Create main.tf (The Main Infrastructure)
&lt;/h3&gt;

&lt;p&gt;This is where the magic happens! We'll build it piece by piece.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano main.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Copy this code:&lt;/strong&gt;&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="c1"&gt;# Terraform configuration&lt;/span&gt;
&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 1.0"&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 5.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="c1"&gt;# Provider configuration&lt;/span&gt;
&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_region&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Data source: Get available availability zones&lt;/span&gt;
&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_availability_zones"&lt;/span&gt; &lt;span class="s2"&gt;"available"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"available"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# VPC&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_cidr&lt;/span&gt;
  &lt;span class="nx"&gt;enable_dns_hostnames&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;enable_dns_support&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-vpc"&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Internet Gateway&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_internet_gateway"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-igw"&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Public Subnet 1&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_subnet"&lt;/span&gt; &lt;span class="s2"&gt;"public_1"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_subnet_1_cidr&lt;/span&gt;
  &lt;span class="nx"&gt;availability_zone&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_availability_zones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;map_public_ip_on_launch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-public-subnet"&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;Type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Public"&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Private Subnet&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_subnet"&lt;/span&gt; &lt;span class="s2"&gt;"private"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnet_cidr&lt;/span&gt;
  &lt;span class="nx"&gt;availability_zone&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_availability_zones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-private-subnet"&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;Type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Private"&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Elastic IP for NAT Gateway&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_eip"&lt;/span&gt; &lt;span class="s2"&gt;"nat"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;domain&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"vpc"&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-nat-eip"&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_internet_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# NAT Gateway&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_nat_gateway"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;allocation_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_eip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-nat-gateway"&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_internet_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Public Route Table&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route_table"&lt;/span&gt; &lt;span class="s2"&gt;"public"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;route&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_block&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="nx"&gt;gateway_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_internet_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-public-rt"&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;Type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Public"&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Private Route Table&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route_table"&lt;/span&gt; &lt;span class="s2"&gt;"private"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;route&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_block&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="nx"&gt;nat_gateway_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_nat_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-private-rt"&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;Type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Private"&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Public Subnet Route Table Association&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route_table_association"&lt;/span&gt; &lt;span class="s2"&gt;"public"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;route_table_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_route_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Private Subnet Route Table Association&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route_table_association"&lt;/span&gt; &lt;span class="s2"&gt;"private_1"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;route_table_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_route_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Private Subnet Route Table Association&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route_table_association"&lt;/span&gt; &lt;span class="s2"&gt;"private"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;route_table_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_route_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Save the file&lt;/strong&gt;&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%2Fi8rhxmkaulj79o71sl2b.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%2Fi8rhxmkaulj79o71sl2b.png" alt=" " width="800" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's the complete VPC infrastructure!&lt;/strong&gt; Let's break it down...&lt;/p&gt;




&lt;h2&gt;
  
  
  🔍 Understanding the Code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Data Source (NEW CONCEPT!)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_availability_zones"&lt;/span&gt; &lt;span class="s2"&gt;"available"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"available"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What's a data source?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resources&lt;/strong&gt; = Things Terraform &lt;strong&gt;creates&lt;/strong&gt; (VPC, subnets, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Sources&lt;/strong&gt; = Things Terraform &lt;strong&gt;reads&lt;/strong&gt; from AWS (AZs, AMIs, etc.)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why use it?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don't hardcode availability zones (they differ by region)&lt;/li&gt;
&lt;li&gt;Automatically get available AZs&lt;/li&gt;
&lt;li&gt;Makes code portable across regions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How to use it:&lt;/strong&gt;&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;availability_zone&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_availability_zones&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# First AZ&lt;/span&gt;
&lt;span class="nx"&gt;availability_zone&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_availability_zones&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Second AZ&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. VPC Resource
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_cidr&lt;/span&gt;
  &lt;span class="nx"&gt;enable_dns_hostnames&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  &lt;span class="c1"&gt;# Important for EC2 instances&lt;/span&gt;
  &lt;span class="nx"&gt;enable_dns_support&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  &lt;span class="c1"&gt;# Important for DNS resolution&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key settings:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;enable_dns_hostnames&lt;/code&gt; = EC2 instances get DNS names&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;enable_dns_support&lt;/code&gt; = DNS resolution works in VPC&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Without these:&lt;/strong&gt; Your instances can't resolve domain names!&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Internet Gateway
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_internet_gateway"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;  &lt;span class="c1"&gt;# Attach to our VPC&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What it does:&lt;/strong&gt; Allows public subnets to access the internet&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Think of it as:&lt;/strong&gt; The front door of your VPC&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Public Subnets
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_subnet"&lt;/span&gt; &lt;span class="s2"&gt;"public_1"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;                  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_subnet_1_cidr&lt;/span&gt;
  &lt;span class="nx"&gt;availability_zone&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_availability_zones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;map_public_ip_on_launch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  &lt;span class="c1"&gt;# Auto-assign public IPs&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key setting:&lt;/strong&gt; &lt;code&gt;map_public_ip_on_launch = true&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EC2 instances in this subnet automatically get public IPs&lt;/li&gt;
&lt;li&gt;Makes them accessible from internet&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Private Subnets
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_subnet"&lt;/span&gt; &lt;span class="s2"&gt;"private_1"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnet_1_cidr&lt;/span&gt;
  &lt;span class="nx"&gt;availability_zone&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_availability_zones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="c1"&gt;# NO map_public_ip_on_launch = Private!&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Notice:&lt;/strong&gt; No &lt;code&gt;map_public_ip_on_launch&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instances in private subnets don't get public IPs&lt;/li&gt;
&lt;li&gt;More secure for databases and internal services&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. Elastic IP and NAT Gateway
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Elastic IP (static public IP)&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_eip"&lt;/span&gt; &lt;span class="s2"&gt;"nat"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;domain&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"vpc"&lt;/span&gt;
  &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_internet_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# NAT Gateway (in public subnet!)&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_nat_gateway"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;allocation_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_eip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;  &lt;span class="c1"&gt;# Must be in public subnet&lt;/span&gt;
  &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_internet_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&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;&lt;strong&gt;What's NAT Gateway?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allows private subnets to access internet (outbound only)&lt;/li&gt;
&lt;li&gt;Private instances can download updates, call APIs&lt;/li&gt;
&lt;li&gt;But internet can't initiate connections to private instances&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why Elastic IP?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NAT Gateway needs a static public IP&lt;/li&gt;
&lt;li&gt;Elastic IP provides that&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;⚠️ COST WARNING:&lt;/strong&gt; NAT Gateway = $0.045/hour (~$32/month)&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Route Tables (The GPS)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Public Route Table:&lt;/strong&gt;&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;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route_table"&lt;/span&gt; &lt;span class="s2"&gt;"public"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;route&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_block&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="c1"&gt;# All traffic&lt;/span&gt;
    &lt;span class="nx"&gt;gateway_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_internet_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;  &lt;span class="c1"&gt;# Goes to Internet Gateway&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;&lt;strong&gt;Translation:&lt;/strong&gt; "Send all internet traffic (0.0.0.0/0) to the Internet Gateway"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Private Route Table:&lt;/strong&gt;&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;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route_table"&lt;/span&gt; &lt;span class="s2"&gt;"private"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;route&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_block&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="c1"&gt;# All traffic&lt;/span&gt;
    &lt;span class="nx"&gt;nat_gateway_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_nat_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;  &lt;span class="c1"&gt;# Goes to NAT Gateway&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;&lt;strong&gt;Translation:&lt;/strong&gt; "Send all internet traffic (0.0.0.0/0) to the NAT Gateway"&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Route Table Associations
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route_table_association"&lt;/span&gt; &lt;span class="s2"&gt;"public_1"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;route_table_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_route_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this does:&lt;/strong&gt; Links subnet to route table&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Think of it as:&lt;/strong&gt; "Public Subnet 1, use the Public Route Table for routing"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without this:&lt;/strong&gt; Subnet doesn't know how to route traffic!&lt;/p&gt;




&lt;h2&gt;
  
  
  📤 Step 5: Create outputs.tf
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano outputs.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Copy this code:&lt;/strong&gt;&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="c1"&gt;# VPC Outputs&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"vpc_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ID of the VPC"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"vpc_cidr"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"CIDR block of the VPC"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cidr_block&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Subnet Outputs&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"public_subnet_1_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ID of public subnet 1"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"public_subnet_2_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ID of public subnet 2"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"private_subnet_1_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ID of private subnet 1"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"private_subnet_2_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ID of private subnet 2"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Gateway Outputs&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"internet_gateway_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ID of the Internet Gateway"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_internet_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"nat_gateway_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ID of the NAT Gateway"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_nat_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"nat_gateway_public_ip"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Public IP of the NAT Gateway"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_eip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_ip&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Route Table Outputs&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"public_route_table_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ID of the public route table"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_route_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"private_route_table_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ID of the private route table"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_route_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Availability Zone&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"availability_zone"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Availability zone used"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_availability_zones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&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;&lt;strong&gt;Save the file&lt;/strong&gt;&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%2Fbacsscc5w2bndu6u87vd.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%2Fbacsscc5w2bndu6u87vd.png" alt=" " width="799" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why so many outputs?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You'll need these IDs in Article 7 (EC2 + Load Balancer)&lt;/li&gt;
&lt;li&gt;Good documentation of what was created&lt;/li&gt;
&lt;li&gt;Easy to reference in other Terraform modules&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🚀 Let's Deploy the VPC!
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 6: Initialize Terraform
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Expected output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;Initializing the backend...
Initializing provider plugins...
&lt;/span&gt;&lt;span class="gp"&gt;- Finding hashicorp/aws versions matching "~&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;5.0&lt;span class="s2"&gt;"...
&lt;/span&gt;&lt;span class="go"&gt;- Installing hashicorp/aws v5.x.x...

Terraform has been successfully initialized!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F58mvko1k1fdf1sniorad.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%2F58mvko1k1fdf1sniorad.png" alt=" " width="799" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 7: Plan the Infrastructure
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What you'll see:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;Terraform&lt;/span&gt; &lt;span class="nx"&gt;will&lt;/span&gt; &lt;span class="nx"&gt;perform&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;following&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

  &lt;span class="c1"&gt;# aws_eip.nat will be created&lt;/span&gt;
  &lt;span class="c1"&gt;# aws_internet_gateway.main will be created&lt;/span&gt;
  &lt;span class="c1"&gt;# aws_nat_gateway.main will be created&lt;/span&gt;
  &lt;span class="c1"&gt;# aws_route_table.private will be created&lt;/span&gt;
  &lt;span class="c1"&gt;# aws_route_table.public will be created&lt;/span&gt;
  &lt;span class="c1"&gt;# aws_route_table_association.private_1 will be created&lt;/span&gt;
  &lt;span class="c1"&gt;# aws_route_table_association.private_2 will be created&lt;/span&gt;
  &lt;span class="c1"&gt;# aws_route_table_association.public_1 will be created&lt;/span&gt;
  &lt;span class="c1"&gt;# aws_route_table_association.public_2 will be created&lt;/span&gt;
  &lt;span class="c1"&gt;# aws_subnet.private_1 will be created&lt;/span&gt;
  &lt;span class="c1"&gt;# aws_subnet.private_2 will be created&lt;/span&gt;
  &lt;span class="c1"&gt;# aws_subnet.public_1 will be created&lt;/span&gt;
  &lt;span class="c1"&gt;# aws_subnet.public_2 will be created&lt;/span&gt;
  &lt;span class="c1"&gt;# aws_vpc.main will be created&lt;/span&gt;

&lt;span class="nx"&gt;Plan&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;change&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;destroy&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fcgly4u9pmll8zx75sfew.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%2Fcgly4u9pmll8zx75sfew.png" alt=" " width="798" height="177"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;10 resources!&lt;/strong&gt; That's a complete VPC infrastructure from just one command.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Review the plan carefully:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ VPC with correct CIDR (10.0.0.0/16)&lt;/li&gt;
&lt;li&gt;✅ 4 subnets with correct CIDRs&lt;/li&gt;
&lt;li&gt;✅ Subnets in different availability zones&lt;/li&gt;
&lt;li&gt;✅ Internet Gateway attached to VPC&lt;/li&gt;
&lt;li&gt;✅ NAT Gateway in public subnet&lt;/li&gt;
&lt;li&gt;✅ Route tables with correct routes&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 8: Apply the Configuration
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;p&gt;Type &lt;code&gt;yes&lt;/code&gt; when prompted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Creating&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Creation&lt;/span&gt; &lt;span class="nx"&gt;complete&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="err"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;vpc-xxxxx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nx"&gt;aws_internet_gateway&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Creating&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_1&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Creating&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_2&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Creating&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_1&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Creating&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_2&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Creating&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;aws_nat_gateway&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Creating&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;aws_nat_gateway&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Still&lt;/span&gt; &lt;span class="nx"&gt;creating&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="nx"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nx"&gt;aws_nat_gateway&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Still&lt;/span&gt; &lt;span class="nx"&gt;creating&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="nx"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;aws_nat_gateway&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Creation&lt;/span&gt; &lt;span class="nx"&gt;complete&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="mi"&gt;1m45&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;

&lt;span class="nx"&gt;Apply&lt;/span&gt; &lt;span class="nx"&gt;complete&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt; &lt;span class="nx"&gt;Resources&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="nx"&gt;added&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="nx"&gt;changed&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="nx"&gt;destroyed&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;

&lt;span class="nx"&gt;Outputs&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

&lt;span class="nx"&gt;availability_zones&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"us-east-1a"&lt;/span&gt;
&lt;span class="nx"&gt;internet_gateway_id&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"igw-xxxxx"&lt;/span&gt;
&lt;span class="nx"&gt;nat_gateway_id&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"nat-xxxxx"&lt;/span&gt;
&lt;span class="nx"&gt;nat_gateway_public_ip&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"54.xxx.xxx.xxx"&lt;/span&gt;
&lt;span class="nx"&gt;private_route_table_id&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"rtb-xxxxx"&lt;/span&gt;
&lt;span class="nx"&gt;private_subnet_id&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"subnet-xxxxx"&lt;/span&gt;
&lt;span class="nx"&gt;public_route_table_id&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"rtb-xxxxx"&lt;/span&gt;
&lt;span class="nx"&gt;public_subnet_id&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"subnet-xxxxx"&lt;/span&gt;
&lt;span class="nx"&gt;vpc_cidr&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&lt;/span&gt;
&lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"vpc-xxxxx"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;⏱️ Time:&lt;/strong&gt; Takes about 2-3 minutes (NAT Gateway is slow to create)&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%2Fjnhcfsjsm9u2gdyy4lbi.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%2Fjnhcfsjsm9u2gdyy4lbi.png" alt=" " width="800" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🎉 Success!&lt;/strong&gt; You just created production-grade networking infrastructure!&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 9: Verify in AWS Console
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Option 1: AWS CLI&lt;/strong&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="c"&gt;# Get VPC ID&lt;/span&gt;
&lt;span class="nv"&gt;VPC_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;terraform output &lt;span class="nt"&gt;-raw&lt;/span&gt; vpc_id&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# List all subnets&lt;/span&gt;
aws ec2 describe-subnets &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--filters&lt;/span&gt; &lt;span class="s2"&gt;"Name=vpc-id,Values=&lt;/span&gt;&lt;span class="nv"&gt;$VPC_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'Subnets[*].[SubnetId,CidrBlock,AvailabilityZone,Tags[?Key==`Name`].Value|[0]]'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Faf4rgbz2xeg4htr2vae1.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%2Faf4rgbz2xeg4htr2vae1.png" alt=" " width="800" height="194"&gt;&lt;/a&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="c"&gt;# Check NAT Gateway&lt;/span&gt;
aws ec2 describe-nat-gateways &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s2"&gt;"Name=vpc-id,Values=&lt;/span&gt;&lt;span class="nv"&gt;$VPC_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'NatGateways[*].[NatGatewayId,State,SubnetId]'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fcgxz1d9exlbybvkxxj6o.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%2Fcgxz1d9exlbybvkxxj6o.png" alt=" " width="800" height="194"&gt;&lt;/a&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="c"&gt;# Check route tables&lt;/span&gt;
aws ec2 describe-route-tables &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--filters&lt;/span&gt; &lt;span class="s2"&gt;"Name=vpc-id,Values=&lt;/span&gt;&lt;span class="nv"&gt;$VPC_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s1"&gt;'RouteTables[*].[RouteTableId,Tags[?Key==`Name`].Value|[0]]'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; table
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fx2jqkdr6joz0hjo8o8nl.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%2Fx2jqkdr6joz0hjo8o8nl.png" alt=" " width="799" height="228"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 2: AWS Console&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to AWS Console → VPC&lt;/li&gt;
&lt;li&gt;Find your VPC: &lt;code&gt;terraform-vpc-vpc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Check:

&lt;ul&gt;
&lt;li&gt;✅ 2 subnets (1 public, 1 private)&lt;/li&gt;
&lt;li&gt;✅ Internet Gateway attached&lt;/li&gt;
&lt;li&gt;✅ NAT Gateway in public subnet&lt;/li&gt;
&lt;li&gt;✅ 2 route tables (public and private)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&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%2Fagljumk3qwbt4tmnukkj.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%2Fagljumk3qwbt4tmnukkj.png" alt=" " width="800" height="221"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Understanding Resource Dependencies
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Implicit Dependencies
&lt;/h3&gt;

&lt;p&gt;Terraform automatically understands dependencies when you reference resources:&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;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_subnet"&lt;/span&gt; &lt;span class="s2"&gt;"public_1"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;  &lt;span class="c1"&gt;# References VPC&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Terraform knows:&lt;/strong&gt; "Create VPC first, then create subnet"&lt;/p&gt;

&lt;h3&gt;
  
  
  Explicit Dependencies
&lt;/h3&gt;

&lt;p&gt;Sometimes you need to force an order:&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;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_eip"&lt;/span&gt; &lt;span class="s2"&gt;"nat"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;domain&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"vpc"&lt;/span&gt;
  &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_internet_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Explicit dependency&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt; Elastic IP in VPC requires Internet Gateway to exist first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependency Graph
&lt;/h3&gt;

&lt;p&gt;Terraform builds a graph of dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VPC
├── Internet Gateway
│   ├── Elastic IP
│   │   └── NAT Gateway
│   └── Public Route Table
│       └── Route Table Associations
└── Subnets
    └── Route Table Associations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Terraform creates resources in the correct order automatically!&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  💰 Cost Breakdown
&lt;/h2&gt;

&lt;p&gt;Let's talk about costs (important!):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Resource&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;VPC&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;FREE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No charge for VPCs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subnets&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;FREE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No charge for subnets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Internet Gateway&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;FREE&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No charge for IGW&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Elastic IP&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;FREE&lt;/strong&gt;*&lt;/td&gt;
&lt;td&gt;*When attached to running instance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NAT Gateway&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.045/hour&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;~$32/month&lt;/strong&gt; ⚠️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data Transfer&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0.045/GB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Through NAT Gateway&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Total for this setup: ~$32/month&lt;/strong&gt; (just for NAT Gateway)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚠️ IMPORTANT:&lt;/strong&gt; Always destroy test infrastructure!&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;&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%2F45buf3zif9w6mk82bz1l.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%2F45buf3zif9w6mk82bz1l.png" alt=" " width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Production Note:&lt;/strong&gt; In production, you'd have one NAT Gateway per AZ (2 NAT Gateways = $64/month) for high availability.&lt;/p&gt;




&lt;h2&gt;
  
  
  🎯 Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. CIDR Planning
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Do:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Plan CIDR blocks before creating VPC&lt;/li&gt;
&lt;li&gt;✅ Leave room for growth (use /16 for VPC)&lt;/li&gt;
&lt;li&gt;✅ Use non-overlapping ranges&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Don't:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Use overlapping CIDR blocks&lt;/li&gt;
&lt;li&gt;❌ Make VPC too small (/24 = only 256 IPs)&lt;/li&gt;
&lt;li&gt;❌ Forget about VPC peering requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tool:&lt;/strong&gt; Use &lt;a href="https://www.ipaddressguide.com/cidr" rel="noopener noreferrer"&gt;CIDR Calculator&lt;/a&gt; for planning&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Multi-AZ Architecture
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Always use multiple availability zones:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ High availability&lt;/li&gt;
&lt;li&gt;✅ Fault tolerance&lt;/li&gt;
&lt;li&gt;✅ Better for production&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Our setup:&lt;/strong&gt; 2 AZs (us-east-1a, us-east-1b)&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Tagging Strategy
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Always tag resources:&lt;/strong&gt;&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;tags&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-vpc"&lt;/span&gt;
  &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
  &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="nx"&gt;Project&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Learning"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy to identify resources&lt;/li&gt;
&lt;li&gt;Cost allocation&lt;/li&gt;
&lt;li&gt;Automation and filtering&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Separate Public and Private
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Security principle:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public subnets: Load balancers, bastion hosts&lt;/li&gt;
&lt;li&gt;Private subnets: Databases, application servers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Never put databases in public subnets!&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Use Variables
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Make everything configurable:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CIDR blocks&lt;/li&gt;
&lt;li&gt;Project name&lt;/li&gt;
&lt;li&gt;Environment&lt;/li&gt;
&lt;li&gt;Region&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Benefit:&lt;/strong&gt; Same code for dev, staging, prod&lt;/p&gt;




&lt;h2&gt;
  
  
  🐛 Common Issues &amp;amp; Solutions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Issue 1: NAT Gateway Creation Timeout
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: timeout while waiting for state to become 'available'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NAT Gateway takes 2-3 minutes to create&lt;/li&gt;
&lt;li&gt;This is normal, just wait&lt;/li&gt;
&lt;li&gt;If it fails, run &lt;code&gt;terraform apply&lt;/code&gt; again&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Issue 2: Elastic IP Limit Reached
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: AddressLimitExceeded: The maximum number of addresses has been reached
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&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="c"&gt;# Check current EIPs&lt;/span&gt;
aws ec2 describe-addresses

&lt;span class="c"&gt;# Release unused EIPs&lt;/span&gt;
aws ec2 release-address &lt;span class="nt"&gt;--allocation-id&lt;/span&gt; eipalloc-xxxxx

&lt;span class="c"&gt;# Or request limit increase in AWS Console&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Issue 3: Route Table Not Associated
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Subnet can't reach internet&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Check route table associations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ec2 describe-route-tables &lt;span class="nt"&gt;--filters&lt;/span&gt; &lt;span class="s2"&gt;"Name=vpc-id,Values=&lt;/span&gt;&lt;span class="nv"&gt;$VPC_ID&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure each subnet has a route table association.&lt;/p&gt;

&lt;h3&gt;
  
  
  Issue 4: CIDR Block Overlap
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: InvalidSubnet.Conflict: The CIDR 'x.x.x.x/x' conflicts with another subnet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check your CIDR blocks don't overlap&lt;/li&gt;
&lt;li&gt;Public: 10.0.1.0/24, 10.0.2.0/24&lt;/li&gt;
&lt;li&gt;Private: 10.0.11.0/24, 10.0.12.0/24&lt;/li&gt;
&lt;li&gt;No overlap!&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Issue 5: Forgot to Destroy NAT Gateway
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Unexpected AWS bill&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&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="c"&gt;# Check for NAT Gateways&lt;/span&gt;
aws ec2 describe-nat-gateways &lt;span class="nt"&gt;--filter&lt;/span&gt; &lt;span class="s2"&gt;"Name=state,Values=available"&lt;/span&gt;

&lt;span class="c"&gt;# Destroy with Terraform&lt;/span&gt;
terraform destroy

&lt;span class="c"&gt;# Or manually delete in AWS Console&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🧹 Cleanup (IMPORTANT!)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;⚠️ Don't forget this step!&lt;/strong&gt; NAT Gateway costs money.&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="c"&gt;# Destroy all resources&lt;/span&gt;
terraform destroy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type &lt;code&gt;yes&lt;/code&gt; when prompted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expected output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;aws_route_table_association&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_2&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Destroying&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;aws_route_table_association&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_1&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Destroying&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;aws_route_table_association&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_2&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Destroying&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;aws_route_table_association&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_1&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Destroying&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;aws_nat_gateway&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Destroying&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;aws_nat_gateway&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Still&lt;/span&gt; &lt;span class="nx"&gt;destroying&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="nx"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nx"&gt;aws_nat_gateway&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Still&lt;/span&gt; &lt;span class="nx"&gt;destroying&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="nx"&gt;elapsed&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;aws_nat_gateway&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Destruction&lt;/span&gt; &lt;span class="nx"&gt;complete&lt;/span&gt; &lt;span class="nx"&gt;after&lt;/span&gt; &lt;span class="mi"&gt;1m30&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;
&lt;span class="err"&gt;...&lt;/span&gt;
&lt;span class="nx"&gt;Destroy&lt;/span&gt; &lt;span class="nx"&gt;complete&lt;/span&gt;&lt;span class="err"&gt;!&lt;/span&gt; &lt;span class="nx"&gt;Resources&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="nx"&gt;destroyed&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F9ipom70vgilwwsxiz48k.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%2F9ipom70vgilwwsxiz48k.png" alt=" " width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verify deletion:&lt;/strong&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="c"&gt;# Should return empty&lt;/span&gt;
aws ec2 describe-vpcs &lt;span class="nt"&gt;--filters&lt;/span&gt; &lt;span class="s2"&gt;"Name=tag:Name,Values=terraform-vpc-vpc"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F6vzad28jaiizf5z5lbpc.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%2F6vzad28jaiizf5z5lbpc.png" alt=" " width="800" height="80"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clean up directory:&lt;/strong&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="nb"&gt;cd&lt;/span&gt; ~
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ~/terraform-vpc-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fqcca9h6pewb84r21gr5u.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%2Fqcca9h6pewb84r21gr5u.png" alt=" " width="762" height="139"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  📚 What You Learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Concepts:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ VPC fundamentals (CIDR, subnets, routing)&lt;/li&gt;
&lt;li&gt;✅ Public vs Private subnets&lt;/li&gt;
&lt;li&gt;✅ Internet Gateway and NAT Gateway&lt;/li&gt;
&lt;li&gt;✅ Route tables and associations&lt;/li&gt;
&lt;li&gt;✅ Data sources (aws_availability_zones)&lt;/li&gt;
&lt;li&gt;✅ Resource dependencies (implicit and explicit)&lt;/li&gt;
&lt;li&gt;✅ Cost-optimized architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Skills:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Created production-ready VPC&lt;/li&gt;
&lt;li&gt;✅ Configured networking components&lt;/li&gt;
&lt;li&gt;✅ Used data sources&lt;/li&gt;
&lt;li&gt;✅ Managed complex infrastructure&lt;/li&gt;
&lt;li&gt;✅ Understood AWS networking costs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ 1 VPC&lt;/li&gt;
&lt;li&gt;✅ 2 Subnets (1 public, 1 private)&lt;/li&gt;
&lt;li&gt;✅ 1 Internet Gateway&lt;/li&gt;
&lt;li&gt;✅ 1 NAT Gateway&lt;/li&gt;
&lt;li&gt;✅ 2 Route Tables&lt;/li&gt;
&lt;li&gt;✅ Cost-optimized setup&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎓 Challenge: Extend Your VPC
&lt;/h2&gt;

&lt;p&gt;Try these modifications:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Easy:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Change CIDR blocks to 172.16.0.0/16&lt;/li&gt;
&lt;li&gt;Add a third availability zone&lt;/li&gt;
&lt;li&gt;Change project name and environment&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Medium:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add more private subnets (10.0.21.0/24, 10.0.22.0/24)&lt;/li&gt;
&lt;li&gt;Create separate route tables for each private subnet&lt;/li&gt;
&lt;li&gt;Add Network ACLs for additional security&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Hard:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add VPC Flow Logs&lt;/li&gt;
&lt;li&gt;Create a second NAT Gateway for high availability&lt;/li&gt;
&lt;li&gt;Add VPC endpoints for S3 and DynamoDB&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🔗 Useful Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/vpc/" rel="noopener noreferrer"&gt;AWS VPC Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc" rel="noopener noreferrer"&gt;Terraform AWS Provider - VPC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ipaddressguide.com/cidr" rel="noopener noreferrer"&gt;CIDR Calculator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/vpc/pricing/" rel="noopener noreferrer"&gt;AWS VPC Pricing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/simplynadaf/terraform-by-sarvar" rel="noopener noreferrer"&gt;GitHub Repo with Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🚀 What's Next?
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;Article 7&lt;/strong&gt;, we'll deploy &lt;strong&gt;EC2 instances and an Application Load Balancer&lt;/strong&gt; in this VPC!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You'll learn:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Launch EC2 instances in public and private subnets&lt;/li&gt;
&lt;li&gt;Configure security groups&lt;/li&gt;
&lt;li&gt;Create an Application Load Balancer&lt;/li&gt;
&lt;li&gt;Set up health checks&lt;/li&gt;
&lt;li&gt;Test the complete architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; 45 minutes&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Difficulty:&lt;/strong&gt; Intermediate&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Outcome:&lt;/strong&gt; Full web application infrastructure&lt;/p&gt;




&lt;h2&gt;
  
  
  📌 Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Thank you for reading. I hope this article provided practical insights and a clearer understanding of the topic.&lt;/p&gt;

&lt;p&gt;If you found this useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❤️ Like if it added value
&lt;/li&gt;
&lt;li&gt;🦄 Unicorn if you’re applying it today
&lt;/li&gt;
&lt;li&gt;💾 Save it for your next optimization session
&lt;/li&gt;
&lt;li&gt;🔄 Share it with your team
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💡 What’s Next
&lt;/h2&gt;

&lt;p&gt;More deep dives are coming soon on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud Operations&lt;/li&gt;
&lt;li&gt;GenAI &amp;amp; Agentic AI&lt;/li&gt;
&lt;li&gt;DevOps Automation&lt;/li&gt;
&lt;li&gt;Data &amp;amp; Platform Engineering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow along for weekly insights and hands-on guides.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌐 Portfolio &amp;amp; Work
&lt;/h2&gt;

&lt;p&gt;You can explore my full body of work, certifications, architecture projects, and technical articles here:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;Visit My Website&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Services I Offer
&lt;/h2&gt;

&lt;p&gt;If you're looking for hands-on guidance or collaboration, I provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud Architecture Consulting (AWS / Azure)&lt;/li&gt;
&lt;li&gt;DevSecOps &amp;amp; Automation Design&lt;/li&gt;
&lt;li&gt;FinOps Optimization Reviews&lt;/li&gt;
&lt;li&gt;Technical Writing (Cloud, DevOps, GenAI)&lt;/li&gt;
&lt;li&gt;Product &amp;amp; Architecture Reviews&lt;/li&gt;
&lt;li&gt;Mentorship &amp;amp; 1:1 Technical Guidance&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🤝 Let’s Connect
&lt;/h2&gt;

&lt;p&gt;I’d love to hear your thoughts. Feel free to drop a comment or connect with me on:&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://www.linkedin.com/in/sarvar04/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For collaborations, consulting, or technical discussions, reach out at:&lt;/p&gt;

&lt;p&gt;📧 &lt;a href="mailto:simplynadaf@gmail.com"&gt;simplynadaf@gmail.com&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Found this helpful? Share it with your team.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;⭐ Star the repo • 📖 Follow the series • 💬 Ask questions  &lt;/p&gt;

&lt;p&gt;Made by &lt;strong&gt;Sarvar Nadaf&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
🌐 &lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;https://sarvarnadaf.com&lt;/a&gt;&lt;/p&gt;




</description>
      <category>terraform</category>
      <category>aws</category>
      <category>devops</category>
      <category>infrastructure</category>
    </item>
    <item>
      <title>Connecting 12 MCP Servers to Amazon Q CLI: What Broke and How I Fixed It</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Mon, 06 Apr 2026 13:02:02 +0000</pubDate>
      <link>https://dev.to/aws-builders/connecting-12-mcp-servers-to-amazon-q-cli-what-broke-and-how-i-fixed-it-48mj</link>
      <guid>https://dev.to/aws-builders/connecting-12-mcp-servers-to-amazon-q-cli-what-broke-and-how-i-fixed-it-48mj</guid>
      <description>&lt;p&gt;👋 Hey there, tech enthusiasts! &lt;/p&gt;

&lt;p&gt;I'm Sarvar, a Cloud Architect with a passion for transforming complex technological challenges into elegant solutions. With extensive experience spanning Cloud Operations (AWS &amp;amp; Azure), Data Operations, Analytics, DevOps, and Generative AI, I've had the privilege of architecting solutions for global enterprises that drive real business impact. Through this article series, I'm excited to share practical insights, best practices, and hands-on experiences from my journey in the tech world. Whether you're a seasoned professional or just starting out, I aim to break down complex concepts into digestible pieces that you can apply in your projects.&lt;/p&gt;

&lt;p&gt;Let's dive in and explore the fascinating world of cloud technology together! 🚀&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Written from experience building AI agent integrations for AWS infrastructure management. Your mileage may vary, but the principles hold across different use cases.&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Do We Still Need MCP When We Have Agent Skills?
&lt;/h1&gt;

&lt;p&gt;As a cloud architect working with AI agents, I've spent the last few months exploring how to extend their capabilities. Specifically, I've been working with Amazon Q Developer Pro - AWS's AI assistant that helps with coding, infrastructure management, and cloud operations through a chat interface.&lt;/p&gt;

&lt;p&gt;This article shares what I learned about two different approaches to extending AI agents: Model Context Protocol (MCP) and Agent Skills. While the specific implementation details are still evolving, the architectural patterns I describe here represent where the ecosystem is heading based on current capabilities and community standards.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem I Was Solving
&lt;/h2&gt;

&lt;p&gt;I needed Amazon Q Developer Pro to help me manage AWS infrastructure across multiple accounts. The agent needed to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check CloudWatch metrics&lt;/li&gt;
&lt;li&gt;Query RDS databases&lt;/li&gt;
&lt;li&gt;Send alerts to Slack&lt;/li&gt;
&lt;li&gt;Generate cost reports&lt;/li&gt;
&lt;li&gt;Review CloudFormation templates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I tried two approaches, and each had serious problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach 1: Connecting Everything via MCP
&lt;/h2&gt;

&lt;p&gt;MCP is a standard protocol that lets AI agents connect to external tools. Think of it like USB ports on your computer - one standard interface that works with many devices.&lt;/p&gt;

&lt;p&gt;I connected 12 MCP servers to Amazon Q Developer Pro:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS CloudWatch server&lt;/li&gt;
&lt;li&gt;RDS query server&lt;/li&gt;
&lt;li&gt;Slack messaging server&lt;/li&gt;
&lt;li&gt;Cost Explorer server&lt;/li&gt;
&lt;li&gt;GitHub server&lt;/li&gt;
&lt;li&gt;And seven more&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What Went Wrong
&lt;/h3&gt;

&lt;p&gt;Before I even asked a question, 35% of the context window was already full. Each MCP server loaded all its available functions into memory. The CloudWatch server alone exposed 15 different functions with detailed parameter descriptions.&lt;/p&gt;

&lt;p&gt;When I asked Amazon Q Developer Pro to "check if our xyz database is healthy," it had to scan through multiple function definitions to figure out which ones to use. Sometimes it picked the wrong ones.&lt;/p&gt;

&lt;p&gt;Every function call and response consumed more context. After three or four operations, I was running out of space for the actual conversation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approach 2: Using Agent Skills
&lt;/h2&gt;

&lt;p&gt;Agent Skills are different. Instead of connecting to external tools, you give the agent domain knowledge - a guide on how to think about a problem.&lt;/p&gt;

&lt;p&gt;I created a skill called "database-health-check" with a simple file structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;database-health-check/
  SKILL.md          (when to use this, what steps to follow)
  scripts/
    check_rds.py    (Python script to query RDS)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SKILL.md file contained:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Database Health Check Skill&lt;/span&gt;

&lt;span class="gu"&gt;## Trigger&lt;/span&gt;
Keywords: "database health", "RDS status", "database performance", "DB issues"

&lt;span class="gu"&gt;## Process&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; Check CPU utilization (threshold: 80%)
&lt;span class="p"&gt;2.&lt;/span&gt; Check active connections (threshold: 90% of max)
&lt;span class="p"&gt;3.&lt;/span&gt; Check replication lag (threshold: 1000ms)
&lt;span class="p"&gt;4.&lt;/span&gt; Check storage space (threshold: 85%)

&lt;span class="gu"&gt;## Decision Logic&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; If CPU &amp;gt; 80%: WARN - "High CPU usage detected"
&lt;span class="p"&gt;-&lt;/span&gt; If connections &amp;gt; 90%: CRITICAL - "Connection pool nearly exhausted"
&lt;span class="p"&gt;-&lt;/span&gt; If replication lag &amp;gt; 1000ms: WARN - "Replication falling behind"
&lt;span class="p"&gt;-&lt;/span&gt; If storage &amp;gt; 85%: CRITICAL - "Low storage space"

&lt;span class="gu"&gt;## Output Format&lt;/span&gt;
Status: [HEALTHY|WARN|CRITICAL]
Issues: [list of problems found]
Recommendations: [suggested actions]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What Went Wrong
&lt;/h3&gt;

&lt;p&gt;The skill worked perfectly on my laptop. Then my colleague tried to use it and got an error: "ModuleNotFoundError: No module named 'boto3'".&lt;/p&gt;

&lt;p&gt;The Python script needed boto3, pandas, and psycopg2. My machine had them installed. His didn't. We had no standard way to declare or install these dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Difference
&lt;/h2&gt;

&lt;p&gt;After working with both, I realized they solve different problems:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MCP answers: "What can I do?"&lt;/strong&gt;&lt;br&gt;
It provides capabilities - functions the agent can call. Each MCP server is self-contained with its own dependencies and environment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skills answer: "How should I think?"&lt;/strong&gt;&lt;br&gt;
They provide expertise - decision logic, quality standards, and workflows. But they run in whatever environment the agent has.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Solution: Use Both Together
&lt;/h2&gt;

&lt;p&gt;Here's what actually works in real time. I'll use a real example from last week.&lt;/p&gt;
&lt;h3&gt;
  
  
  Example: Automated Cost Anomaly Detection
&lt;/h3&gt;

&lt;p&gt;I needed Amazon Q Developer Pro to monitor our AWS costs and alert us when something unusual happens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The MCP Layer (The Hands)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I set up two MCP servers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;aws-cost-explorer-mcp - exposes functions like get_daily_costs(), get_service_breakdown()&lt;/li&gt;
&lt;li&gt;slack-notifications-mcp - exposes send_message(), create_incident()&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each server runs in its own Docker container with all dependencies installed. Amazon Q Developer Pro doesn't need to know about boto3 or the Slack SDK.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Skill Layer (The Brain)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I created a cost-monitoring skill with this logic in SKILL.md:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Trigger: When user asks about cost anomalies or unusual spending

Process:
&lt;span class="p"&gt;1.&lt;/span&gt; Get last 30 days of daily costs
&lt;span class="p"&gt;2.&lt;/span&gt; Calculate average and standard deviation
&lt;span class="p"&gt;3.&lt;/span&gt; Check if today's cost is more than 2 standard deviations above average
&lt;span class="p"&gt;4.&lt;/span&gt; If yes, get service breakdown to identify which service spiked
&lt;span class="p"&gt;5.&lt;/span&gt; If spike is over $500, send high-priority Slack alert
&lt;span class="p"&gt;6.&lt;/span&gt; If spike is under $500, just report in conversation

Quality checks:
&lt;span class="p"&gt;-&lt;/span&gt; Always compare against same day of week (Monday vs Monday)
&lt;span class="p"&gt;-&lt;/span&gt; Exclude known scheduled events (monthly backups, etc.)
&lt;span class="p"&gt;-&lt;/span&gt; Include percentage change, not just absolute numbers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How They Work Together&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When I ask Amazon Q Developer Pro: "Are our AWS costs normal today?"&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Amazon Q Developer Pro matches the question to the cost-monitoring skill&lt;/li&gt;
&lt;li&gt;The skill loads its SKILL.md (only 2KB of context)&lt;/li&gt;
&lt;li&gt;The skill requires cost data, so Amazon Q Developer Pro connects to the aws-cost-explorer-mcp server&lt;/li&gt;
&lt;li&gt;The skill orchestrates: call get_daily_costs(), analyze the data, decide if it's anomalous&lt;/li&gt;
&lt;li&gt;If anomalous, the skill calls the slack-notifications-mcp server&lt;/li&gt;
&lt;li&gt;After completion, the skill unloads from memory&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The MCP servers handle the technical execution. The skill handles the business logic and decision-making.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Architecture Works
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Context Efficiency&lt;/strong&gt;&lt;br&gt;
Only the active skill loads into memory. MCP tools load on-demand when the skill needs them. I went from 35% context consumed at startup to 5%.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Portability&lt;/strong&gt;&lt;br&gt;
The same skill works on my laptop, my colleague's Windows machine, and our CI/CD pipeline. The MCP servers can run locally, in containers, or as remote services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reusability&lt;/strong&gt;&lt;br&gt;
The aws-cost-explorer-mcp server is used by three different skills:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;cost-monitoring (detects anomalies)&lt;/li&gt;
&lt;li&gt;budget-planning (forecasts spending)&lt;/li&gt;
&lt;li&gt;cost-optimization (finds savings opportunities)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each skill brings different expertise, but they share the same data source.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Maintainability&lt;/strong&gt;&lt;br&gt;
When AWS changes their Cost Explorer API, I update one MCP server. All skills continue working. When business rules change (new alert thresholds), I update the skill. The MCP servers don't need to change.&lt;/p&gt;
&lt;h2&gt;
  
  
  Visual Overview
&lt;/h2&gt;

&lt;p&gt;Here's how the three layers work together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────┐
│   Agent Layer (Amazon Q Developer Pro)  │
│   Matches tasks → Loads skills          │
│   Connects to MCP servers               │
└─────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────┐
│         Skill Layer (The Brain)         │
│   cost-monitoring.SKILL.md              │
│   - When to trigger                     │
│   - Decision logic                      │
│   - Quality checks                      │
└─────────────────────────────────────────┘
                    ↓
┌─────────────────────────────────────────┐
│         MCP Layer (The Hands)           │
│   aws-cost-explorer-mcp                 │
│   slack-notifications-mcp               │
│   - Tool execution                      │
│   - Environment isolation               │
└─────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A Practical Pattern
&lt;/h2&gt;

&lt;p&gt;Here's the decision framework I use:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Put it in an MCP server if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple skills will use it&lt;/li&gt;
&lt;li&gt;It has heavy dependencies (Python libraries, system tools)&lt;/li&gt;
&lt;li&gt;It needs credentials or secrets&lt;/li&gt;
&lt;li&gt;It's a stable, reusable capability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Put it in a skill if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's domain knowledge or business logic&lt;/li&gt;
&lt;li&gt;It orchestrates multiple tools&lt;/li&gt;
&lt;li&gt;It has conditional decision-making&lt;/li&gt;
&lt;li&gt;It's specific to one workflow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Keep it as a simple script if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It's a one-time operation&lt;/li&gt;
&lt;li&gt;The entire workflow is tightly coupled&lt;/li&gt;
&lt;li&gt;Splitting it would add unnecessary complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's Still Missing
&lt;/h2&gt;

&lt;p&gt;The ecosystem isn't mature yet. Here's what I wish existed:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dependency Declaration&lt;/strong&gt;&lt;br&gt;
Skills need a standard way to say "I need these capabilities" without hardcoding specific MCP servers. Something like:&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="na"&gt;requires&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cloud_metrics&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;notifications&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the runtime figures out which MCP servers provide those capabilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dynamic Loading&lt;/strong&gt;&lt;br&gt;
Right now, when you connect an MCP server, all its tools load immediately. I want skills to control this: "Load only the cost analysis tools for this task."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Graceful Fallback&lt;/strong&gt;&lt;br&gt;
If an MCP server is unavailable, the skill should automatically fall back to a built-in script or tell me clearly what's missing.&lt;/p&gt;

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

&lt;p&gt;After six months of exploring these patterns with Amazon Q Developer Pro and other AI agents, here's what I know:&lt;/p&gt;

&lt;p&gt;MCP and Agent Skills aren't competing approaches. They're complementary layers of the same system.&lt;/p&gt;

&lt;p&gt;MCP gives your agent reliable, isolated capabilities. Skills give your agent the expertise to use those capabilities intelligently.&lt;/p&gt;

&lt;p&gt;You need both. MCP without skills is a toolbox with no craftsman. Skills without MCP are expertise with unreliable tools.&lt;/p&gt;

&lt;p&gt;The architecture that works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Skills encode what to do and when&lt;/li&gt;
&lt;li&gt;MCP servers provide how to do it&lt;/li&gt;
&lt;li&gt;The agent runtime connects them together&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isn't just theoretical. These patterns are being implemented in actual environments, managing real AWS infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;If you want to explore this architecture:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For MCP:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check the official MCP specification at modelcontextprotocol.io&lt;/li&gt;
&lt;li&gt;Browse existing MCP servers at github.com/modelcontextprotocol/servers&lt;/li&gt;
&lt;li&gt;Amazon Q Developer supports MCP through the Q CLI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For Agent Skills:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Review the Agent Skills specification by Anthropic&lt;/li&gt;
&lt;li&gt;Start with simple skills that encode your team's operational knowledge&lt;/li&gt;
&lt;li&gt;Focus on decision logic and workflows, not heavy computation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Next Steps:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Identify one repetitive task you do with your AI agent&lt;/li&gt;
&lt;li&gt;Ask: Does this need external tools (MCP) or decision logic (Skill)?&lt;/li&gt;
&lt;li&gt;Build the simplest version that works&lt;/li&gt;
&lt;li&gt;Iterate based on what you learn&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The ecosystem is still maturing, but the core patterns are solid. Start small, learn from erros, and build up from there.&lt;/p&gt;




&lt;h2&gt;
  
  
  📌 Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Thank you for reading! I hope this article gave you practical insights and a clearer perspective on the topic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Was this helpful?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❤️ Like if it added value&lt;/li&gt;
&lt;li&gt;🦄 Unicorn if you’re applying it today&lt;/li&gt;
&lt;li&gt;💾 Save for your next optimization session&lt;/li&gt;
&lt;li&gt;🔄 Share with your team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Follow me for more on:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS architecture patterns&lt;/li&gt;
&lt;li&gt;FinOps automation&lt;/li&gt;
&lt;li&gt;Multi-account strategies&lt;/li&gt;
&lt;li&gt;AI-driven DevOps&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💡 What’s Next
&lt;/h2&gt;

&lt;p&gt;More deep dives coming soon on cloud operations, GenAI, Agentic-AI, DevOps, and data workflows follow for weekly insights.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌐 Portfolio &amp;amp; Work
&lt;/h2&gt;

&lt;p&gt;You can explore my full body of work, certifications, architecture projects, and technical articles here:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;Visit My Website&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Services I Offer
&lt;/h2&gt;

&lt;p&gt;If you're looking for hands-on guidance or collaboration, I provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud Architecture Consulting (AWS / Azure)&lt;/li&gt;
&lt;li&gt;DevSecOps &amp;amp; Automation Design&lt;/li&gt;
&lt;li&gt;FinOps Optimization Reviews&lt;/li&gt;
&lt;li&gt;Technical Writing (Cloud, DevOps, GenAI)&lt;/li&gt;
&lt;li&gt;Product &amp;amp; Architecture Reviews&lt;/li&gt;
&lt;li&gt;Mentorship &amp;amp; 1:1 Technical Guidance&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🤝 Let’s Connect
&lt;/h2&gt;

&lt;p&gt;I’d love to hear your thoughts drop a comment or connect with me on &lt;a href="https://www.linkedin.com/in/sarvar04/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For collaborations, consulting, or technical discussions, feel free to reach out directly at &lt;a href="mailto:simplynadaf@gmail.com"&gt;simplynadaf@gmail.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Happy Learning&lt;/strong&gt; 🚀&lt;/p&gt;

</description>
      <category>aws</category>
      <category>ai</category>
      <category>discuss</category>
      <category>mcp</category>
    </item>
    <item>
      <title>Terraform Variables and Outputs: Making Your Infrastructure Flexible</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Wed, 01 Apr 2026 09:59:13 +0000</pubDate>
      <link>https://dev.to/aws-builders/terraform-variables-and-outputs-making-your-infrastructure-flexible-3ebc</link>
      <guid>https://dev.to/aws-builders/terraform-variables-and-outputs-making-your-infrastructure-flexible-3ebc</guid>
      <description>&lt;p&gt;👋 Hey there, tech enthusiasts! &lt;/p&gt;

&lt;p&gt;I'm Sarvar, a Cloud Architect with a passion for transforming complex technological challenges into elegant solutions. With extensive experience spanning Cloud Operations (AWS &amp;amp; Azure), Data Operations, Analytics, DevOps, and Generative AI, I've had the privilege of architecting solutions for global enterprises that drive real business impact. Through this article series, I'm excited to share practical insights, best practices, and hands-on experiences from my journey in the tech world. Whether you're a seasoned professional or just starting out, I aim to break down complex concepts into digestible pieces that you can apply in your projects.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"The difference between a script and reusable infrastructure code is variables."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🎯 Welcome Back!
&lt;/h2&gt;

&lt;p&gt;Remember in &lt;a href="https://dev.to/aws-builders/your-first-infrastructure-as-code-in-four-commands-46ep"&gt;Article 3&lt;/a&gt; when you created that S3 bucket? You hardcoded the bucket name directly in the code:&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;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"my_first_bucket"&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-first-bucket-yourname-2026"&lt;/span&gt;  &lt;span class="c1"&gt;# Hardcoded!&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;That works... once.&lt;/strong&gt; But what if you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10 buckets with different names?&lt;/li&gt;
&lt;li&gt;Same code for dev, staging, and production?&lt;/li&gt;
&lt;li&gt;To let your team customize configurations?&lt;/li&gt;
&lt;li&gt;To reuse this code in different projects?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Today, you'll learn how to make your Terraform code flexible and reusable&lt;/strong&gt; using variables and outputs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;By the end of this article, you'll:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Convert hardcoded values to variables&lt;/li&gt;
&lt;li&gt;✅ Use all variable types (string, number, bool, list, map)&lt;/li&gt;
&lt;li&gt;✅ Work with &lt;code&gt;terraform.tfvars&lt;/code&gt; files&lt;/li&gt;
&lt;li&gt;✅ Create outputs to expose information&lt;/li&gt;
&lt;li&gt;✅ Build dev/prod configurations from the same code&lt;/li&gt;
&lt;li&gt;✅ Use variable validation and sensitive values&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Time Required:&lt;/strong&gt; 30 minutes&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Cost:&lt;/strong&gt; $0 (using free tier resources)&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Difficulty:&lt;/strong&gt; Beginner-friendly&lt;/p&gt;

&lt;p&gt;Let's make your code reusable! 🚀&lt;/p&gt;


&lt;h2&gt;
  
  
  💔 The Problem: Hardcoded Hell
&lt;/h2&gt;
&lt;h3&gt;
  
  
  The Painful Reality
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Scenario 1: Multiple Environments&lt;/strong&gt;&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="c1"&gt;# dev.tf&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"app"&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;"myapp-dev-bucket"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# staging.tf (copy-paste and change)&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"app"&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;"myapp-staging-bucket"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# prod.tf (copy-paste and change again)&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"app"&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;"myapp-prod-bucket"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Problems:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Three copies of the same code&lt;/li&gt;
&lt;li&gt;❌ Change one thing = update three files&lt;/li&gt;
&lt;li&gt;❌ High risk of mistakes&lt;/li&gt;
&lt;li&gt;❌ Nightmare to maintain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Scenario 2: Team Collaboration&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer 1: "I need the bucket name to be X"
Developer 2: "I need it to be Y"
DevOps: "Production needs Z"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Everyone edits the same &lt;code&gt;.tf&lt;/code&gt; file = merge conflicts and chaos!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There's a better way.&lt;/strong&gt; Enter variables.&lt;/p&gt;


&lt;h2&gt;
  
  
  🌟 What Are Variables?
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Simple Definition
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Variables are like function parameters for your infrastructure code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Think of it like this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without Variables (Hardcoded):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createBucket&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-hardcoded-bucket-name&lt;/span&gt;&lt;span class="dl"&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;&lt;strong&gt;With Variables (Flexible):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createBucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;bucketName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;createBucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dev-bucket&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="c1"&gt;// Different uses&lt;/span&gt;
&lt;span class="nf"&gt;createBucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prod-bucket&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// Same code!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;In Terraform:&lt;/strong&gt;&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="c1"&gt;# Define the variable&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"bucket_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Name of the S3 bucket"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Use the variable&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"app"&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="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket_name&lt;/span&gt;  &lt;span class="c1"&gt;# Reference with var.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Same code, different values!&lt;/strong&gt; That's the power of variables.&lt;/p&gt;


&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;The complete source code and Terraform configuration used in this article can be found on GitHub:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/simplynadaf" rel="noopener noreferrer"&gt;
        simplynadaf
      &lt;/a&gt; / &lt;a href="https://github.com/simplynadaf/terraform-by-sarvar" rel="noopener noreferrer"&gt;
        terraform-by-sarvar
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Terraform Series
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🚀 Terraform By Sarvar&lt;/h1&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Complete Terraform tutorial series from basics to advanced concepts&lt;/h3&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://www.terraform.io/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/446abdaa5e617237549619105d10cbd99f693a057dc7b366edd5256283015a75/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5465727261666f726d2d76312e31342b2d3632334345343f7374796c653d666f722d7468652d6261646765266c6f676f3d7465727261666f726d266c6f676f436f6c6f723d7768697465" alt="Terraform"&gt;&lt;/a&gt;
&lt;a href="https://aws.amazon.com/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/0b14c31424193ad1651b7422c0c3fd0f5339f3f2632f5ecc3d5313c83e8aeba0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4157532d436c6f75642d4646393930303f7374796c653d666f722d7468652d6261646765266c6f676f3d616d617a6f6e2d617773266c6f676f436f6c6f723d7768697465" alt="AWS"&gt;&lt;/a&gt;
&lt;a href="https://github.com/simplynadaf/terraform-by-sarvar/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/9218332452902d9e542a100d0af126fd3174a116456614d2cf093546a13783db/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d677265656e2e7376673f7374796c653d666f722d7468652d6261646765" alt="License"&gt;&lt;/a&gt;
&lt;a href="https://dev.to/sarvar_04/series/36958" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/ad1f81014cac91cbb305e20c6fb83301ac4992821ede633a6a83bda4379ed118/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6465762e746f2d5365726965732d3041304130413f7374796c653d666f722d7468652d6261646765266c6f676f3d6465762e746f266c6f676f436f6c6f723d7768697465" alt="dev.to"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://dev.to/sarvar_04/series/36963" rel="nofollow"&gt;📖 Read the Series&lt;/a&gt; • &lt;a href="https://sarvarnadaf.com" rel="nofollow noopener noreferrer"&gt;🌐 Portfolio&lt;/a&gt; • &lt;a href="https://www.linkedin.com/in/sarvar04/" rel="nofollow noopener noreferrer"&gt;💼 LinkedIn&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📚 Series Overview&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;This repository contains &lt;strong&gt;ONLY Terraform code examples&lt;/strong&gt; for the &lt;strong&gt;Terraform By Sarvar&lt;/strong&gt; tutorial series.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;⚠️ IMPORTANT:&lt;/strong&gt; This repo contains only &lt;code&gt;.tf&lt;/code&gt; files and infrastructure code. Articles are published on dev.to, not stored here.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;📖 &lt;strong&gt;Read the series on dev.to:&lt;/strong&gt; &lt;a href="https://dev.to/sarvar_04/series/36963" rel="nofollow"&gt;https://dev.to/sarvar_04/series/36963&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;🎯 What You'll Learn&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;✅ Infrastructure as Code (IaC) fundamentals&lt;/li&gt;
&lt;li&gt;✅ Terraform basics to advanced concepts&lt;/li&gt;
&lt;li&gt;✅ AWS resource provisioning&lt;/li&gt;
&lt;li&gt;✅ Best practices and real-world patterns&lt;/li&gt;
&lt;li&gt;✅ Production-ready configurations&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;📊 Series Progress&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/5d15589585043bcdb6defccdfdd8103a9e0c81e1d32502a3aee5acca8cfe3462/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f41727469636c65732d3725324631302d626c75653f7374796c653d666c61742d737175617265"&gt;&lt;img src="https://camo.githubusercontent.com/5d15589585043bcdb6defccdfdd8103a9e0c81e1d32502a3aee5acca8cfe3462/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f41727469636c65732d3725324631302d626c75653f7374796c653d666c61742d737175617265" alt="Progress"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/ab5bccc65fd0dfc759cb04ffe3446775f8674a09e936f473d7a51abcd3e02713/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5374617475732d4163746976652d737563636573733f7374796c653d666c61742d737175617265"&gt;&lt;img src="https://camo.githubusercontent.com/ab5bccc65fd0dfc759cb04ffe3446775f8674a09e936f473d7a51abcd3e02713/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5374617475732d4163746976652d737563636573733f7374796c653d666c61742d737175617265" alt="Status"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📖 Article Series&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;📘 Foundation (Articles 1-5) ✅ Complete&lt;/h3&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aws-builders/why-every-developer-should-learn-terraform-in-2026-and-how-to-start--4fk0" rel="nofollow"&gt;Introduction to Terraform &amp;amp; IaC&lt;/a&gt; - ✅ Published&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aws-builders/installing-terraform-and-setting-up-your-environment-1j9b" rel="nofollow"&gt;Installation &amp;amp; Setup&lt;/a&gt; - ✅ Published&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aws-builders/your-first-infrastructure-as-code-in-four-commands-46ep" rel="nofollow"&gt;Your First AWS Resource&lt;/a&gt; - ✅ Published&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aws-builders/terraform-state-the-one-file-you-cant-afford-to-lose-33l4" rel="nofollow"&gt;Understanding Terraform State&lt;/a&gt; - ✅ Published&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aws-builders/terraform-variables-and-outputs-making-your-infrastructure-flexible-3ebc" rel="nofollow"&gt;Variables and Outputs&lt;/a&gt; - ✅ Published&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;📗 Real Infrastructure (Articles 6-10)&lt;/h3&gt;

&lt;/div&gt;
&lt;ol start="6"&gt;
&lt;li&gt;
&lt;a href="https://dev.to/aws-builders/building-your-first-vpc-aws-networking-with-terraform-3h34" rel="nofollow"&gt;Building a VPC from Scratch&lt;/a&gt;…&lt;/li&gt;
&lt;/ol&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/simplynadaf/terraform-by-sarvar" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;





&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;p&gt;Follow these steps to run the project on your local machine:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Clone the repository:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/simplynadaf/terraform-by-sarvar.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Navigate to the project directory:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;terraform-by-sarvar/articles/08-lambda-serverless
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🛠️ Hands-On: Your First Variable
&lt;/h2&gt;

&lt;p&gt;Let's convert our hardcoded S3 bucket to use variables.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create Project Directory
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ~/terraform-variables-demo
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/terraform-variables-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fr175ghbc7sfdr4p7tjbi.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%2Fr175ghbc7sfdr4p7tjbi.png" alt=" " width="800" height="110"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Create variables.tf
&lt;/h3&gt;

&lt;p&gt;Create a new file called &lt;code&gt;variables.tf&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;nano variables.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Add this code:&lt;/strong&gt;&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="c1"&gt;# String variable - for text values&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"aws_region"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS region where resources will be created"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&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="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"bucket_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Name of the S3 bucket (must be globally unique)"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="c1"&gt;# No default - user must provide this value&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Map variable - for key-value pairs like tags&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"common_tags"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Common tags to apply to all resources"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Development"&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
    &lt;span class="nx"&gt;Project&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Learning"&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;&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%2Fetvsmjmrsbdecyol4u0o.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%2Fetvsmjmrsbdecyol4u0o.png" alt=" " width="800" height="70"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understanding the syntax:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;description&lt;/code&gt; - Explains what the variable is for (always add this!)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;type&lt;/code&gt; - What kind of value (string, number, bool, list, map)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;default&lt;/code&gt; - Optional default value (if not provided, user must supply it)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 3: Create main.tf
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano main.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Add this code:&lt;/strong&gt;&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;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 1.0"&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 5.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="c1"&gt;# Use variable in provider&lt;/span&gt;
&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_region&lt;/span&gt;  &lt;span class="c1"&gt;# Reference variable with var.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Use variables in resource&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"example"&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="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket_name&lt;/span&gt;  &lt;span class="c1"&gt;# Variable for bucket name&lt;/span&gt;

  &lt;span class="c1"&gt;# Merge common_tags with specific tags&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;common_tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket_name&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;&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%2Flov5ql9kzymbm7102qog.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%2Flov5ql9kzymbm7102qog.png" alt=" " width="800" height="87"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notice:&lt;/strong&gt; We reference variables with &lt;code&gt;var.variable_name&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Initialize and Test
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Terraform will prompt you:&lt;/strong&gt;&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;var&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket_name&lt;/span&gt;
  &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="nx"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;S3&lt;/span&gt; &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;must&lt;/span&gt; &lt;span class="nx"&gt;be&lt;/span&gt; &lt;span class="nx"&gt;globally&lt;/span&gt; &lt;span class="nx"&gt;unique&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;Enter&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F0uvoffgtufuxvj3iloio.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%2F0uvoffgtufuxvj3iloio.png" alt=" " width="799" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Type:&lt;/strong&gt; &lt;code&gt;my-first-variable-bucket-yourname-2026&lt;/code&gt; and press Enter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You'll see the plan:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="nx"&gt;Terraform&lt;/span&gt; &lt;span class="nx"&gt;will&lt;/span&gt; &lt;span class="nx"&gt;perform&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;following&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;

  &lt;span class="c1"&gt;# aws_s3_bucket.example will be created&lt;/span&gt;
  &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="err"&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;"my-first-variable-bucket-yourname-2026"&lt;/span&gt;
      &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;tags&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"Environment"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Development"&lt;/span&gt;
          &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"ManagedBy"&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
          &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"Name"&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-first-variable-bucket-yourname-2026"&lt;/span&gt;
          &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="s2"&gt;"Project"&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Learning"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;Plan&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;change&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt; &lt;span class="nx"&gt;destroy&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fjbfk838xhhbwetmx7pdm.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%2Fjbfk838xhhbwetmx7pdm.png" alt=" " width="799" height="380"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't apply yet!&lt;/strong&gt; Let's learn better ways to pass variables.&lt;/p&gt;




&lt;h2&gt;
  
  
  📝 Three Ways to Pass Variables
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Method 1: Command Line (What We Just Did)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform plan
&lt;span class="c"&gt;# Terraform prompts for bucket_name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; Interactive&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; Tedious for multiple variables&lt;/p&gt;
&lt;h3&gt;
  
  
  Method 2: Command Line Flags
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform plan &lt;span class="nt"&gt;-var&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"bucket_name=my-bucket-2026"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; Good for CI/CD pipelines&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; Long commands with many variables&lt;/p&gt;
&lt;h3&gt;
  
  
  Method 3: terraform.tfvars File (Best for Most Cases)
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;terraform.tfvars&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;nano terraform.tfvars
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Add this:&lt;/strong&gt;&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;bucket_name&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-variable-bucket-yourname-2026"&lt;/span&gt;
&lt;span class="nx"&gt;aws_region&lt;/span&gt;  &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;

&lt;span class="nx"&gt;common_tags&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Development"&lt;/span&gt;
  &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="nx"&gt;Project&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Variables Demo"&lt;/span&gt;
  &lt;span class="nx"&gt;Owner&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"YourName"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fqeablxn4uhnkuotlgrvg.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%2Fqeablxn4uhnkuotlgrvg.png" alt=" " width="800" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now run:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;No prompts!&lt;/strong&gt; Terraform automatically loads &lt;code&gt;terraform.tfvars&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Apply it:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Type &lt;code&gt;yes&lt;/code&gt; when prompted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🎉 Success!&lt;/strong&gt; You just created infrastructure using variables!&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%2F49332euimjyuq1la6j58.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%2F49332euimjyuq1la6j58.png" alt=" " width="800" height="282"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🎨 Variable Types: The Complete Guide
&lt;/h2&gt;

&lt;p&gt;Terraform supports multiple variable types. Let's explore them all!&lt;/p&gt;

&lt;h3&gt;
  
  
  Update variables.tf
&lt;/h3&gt;

&lt;p&gt;Replace your &lt;code&gt;variables.tf&lt;/code&gt; with this comprehensive version:&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="c1"&gt;# 1. STRING - Text values&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"aws_region"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS region where resources will be created"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&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="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"bucket_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Name of the S3 bucket (must be globally unique)"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"bucket_prefix"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Prefix for multiple bucket names"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-demo"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# 2. NUMBER - Numeric values&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"bucket_count"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Number of S3 buckets to create"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;

  &lt;span class="c1"&gt;# Validation rule&lt;/span&gt;
  &lt;span class="nx"&gt;validation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;condition&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket_count&lt;/span&gt; &lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket_count&lt;/span&gt; &lt;span class="err"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;
    &lt;span class="nx"&gt;error_message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Bucket count must be between 1 and 5."&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# 3. BOOLEAN - True/False values&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"enable_versioning"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Enable versioning on the S3 bucket"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bool&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# 4. LIST - Ordered collection of values&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"allowed_ips"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"List of IP addresses allowed to access the bucket"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;default&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="c1"&gt;# 5. MAP - Key-value pairs&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"common_tags"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Common tags to apply to all resources"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Development"&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
    &lt;span class="nx"&gt;Project&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Learning"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# 6. ENVIRONMENT - With validation&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"environment"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Environment name (dev, staging, prod)"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt;

  &lt;span class="nx"&gt;validation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;condition&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"staging"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"prod"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;error_message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Environment must be dev, staging, or prod."&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;h3&gt;
  
  
  Update main.tf to Use All Variable Types
&lt;/h3&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;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 1.0"&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 5.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;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_region&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Main S3 bucket with variables&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"example"&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="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket_name&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;common_tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket_name&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="c1"&gt;# Conditional resource - only created if enable_versioning is true&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket_versioning"&lt;/span&gt; &lt;span class="s2"&gt;"example"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enable_versioning&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;  &lt;span class="c1"&gt;# Conditional creation&lt;/span&gt;
  &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_s3_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="nx"&gt;versioning_configuration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Enabled"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Multiple buckets using count (number variable)&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"multiple"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;count&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket_count&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;"${var.bucket_prefix}-${count.index + 1}-yourname-2026"&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;common_tags&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What's happening here:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;var.enable_versioning ? 1 : 0&lt;/code&gt; - Conditional: if true, create 1 resource; if false, create 0&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;count = var.bucket_count&lt;/code&gt; - Create multiple resources based on number&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;count.index&lt;/code&gt; - Access the current index (0, 1, 2...)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;merge()&lt;/code&gt; - Combine two maps together&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📤 Outputs: Exposing Information
&lt;/h2&gt;

&lt;p&gt;Variables let data IN. Outputs let data OUT.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Outputs Matter
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use cases:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Display important information after &lt;code&gt;terraform apply&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Pass values to other Terraform modules&lt;/li&gt;
&lt;li&gt;Use in scripts or CI/CD pipelines&lt;/li&gt;
&lt;li&gt;Share information with team members&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Create outputs.tf
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano outputs.tf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Add this code:&lt;/strong&gt;&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="c1"&gt;# Basic outputs&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"bucket_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Name of the created S3 bucket"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_s3_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"bucket_arn"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ARN of the S3 bucket"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_s3_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"bucket_region"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Region where the bucket was created"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_s3_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"bucket_domain_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Domain name of the bucket"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_s3_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket_domain_name&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Multiple buckets output&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"multiple_bucket_names"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Names of all created buckets"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_s3_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;multiple&lt;/span&gt;&lt;span class="p"&gt;[*].&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Conditional output&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"versioning_enabled"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Whether versioning is enabled"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enable_versioning&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Map output&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"bucket_tags"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Tags applied to the bucket"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_s3_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Sensitive output example&lt;/span&gt;
&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"bucket_id_sensitive"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Bucket ID (marked as sensitive)"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_s3_bucket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;sensitive&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  &lt;span class="c1"&gt;# Won't display in plan/apply output&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F075lobp3ssdxq9s36ryr.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%2F075lobp3ssdxq9s36ryr.png" alt=" " width="799" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Update terraform.tfvars
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;bucket_name&lt;/span&gt;      &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-variable-bucket-yourname-2026"&lt;/span&gt;
&lt;span class="nx"&gt;bucket_prefix&lt;/span&gt;    &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"demo-bucket-yourname"&lt;/span&gt;
&lt;span class="nx"&gt;bucket_count&lt;/span&gt;     &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="nx"&gt;enable_versioning&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="nx"&gt;aws_region&lt;/span&gt;       &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;

&lt;span class="nx"&gt;common_tags&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Development"&lt;/span&gt;
  &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="nx"&gt;Project&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Variables Demo"&lt;/span&gt;
  &lt;span class="nx"&gt;Owner&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"YourName"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Apply and See Outputs
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After applying, you'll see:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

Outputs:

bucket_arn = "arn:aws:s3:::my-variable-bucket-yourname-2026"
bucket_domain_name = "my-variable-bucket-yourname-2026.s3.amazonaws.com"
bucket_id_sensitive = &amp;lt;sensitive&amp;gt;
bucket_name = "my-variable-bucket-yourname-2026"
bucket_region = "us-east-1"
bucket_tags = tomap({
  "Environment" = "Development"
  "ManagedBy" = "Terraform"
  "Name" = "my-variable-bucket-yourname-2026"
  "Owner" = "YourName"
  "Project" = "Variables Demo"
})
multiple_bucket_names = [
  "demo-bucket-yourname-1-yourname-2026",
  "demo-bucket-yourname-2-yourname-2026",
]
versioning_enabled = true
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fx9ruu0jp0nf7dwhc00qd.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%2Fx9ruu0jp0nf7dwhc00qd.png" alt=" " width="800" height="390"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  View Outputs Anytime
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# View all outputs&lt;/span&gt;
terraform output

&lt;span class="c"&gt;# View specific output&lt;/span&gt;
terraform output bucket_name

&lt;span class="c"&gt;# View sensitive output (will reveal the value)&lt;/span&gt;
terraform output bucket_id_sensitive

&lt;span class="c"&gt;# Output as JSON (useful for scripts)&lt;/span&gt;
terraform output &lt;span class="nt"&gt;-json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🌍 Real-World: Dev vs Prod Configurations
&lt;/h2&gt;

&lt;p&gt;The real power of variables: &lt;strong&gt;same code, different configurations!&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Create dev.tfvars
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano dev.tfvars
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Development environment configuration&lt;/span&gt;
&lt;span class="nx"&gt;environment&lt;/span&gt;       &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"dev"&lt;/span&gt;
&lt;span class="nx"&gt;bucket_name&lt;/span&gt;       &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"myapp-dev-yourname-2026"&lt;/span&gt;
&lt;span class="nx"&gt;bucket_prefix&lt;/span&gt;     &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"myapp-dev-yourname"&lt;/span&gt;
&lt;span class="nx"&gt;bucket_count&lt;/span&gt;      &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;span class="nx"&gt;enable_versioning&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;  &lt;span class="c1"&gt;# Save costs in dev&lt;/span&gt;
&lt;span class="nx"&gt;aws_region&lt;/span&gt;        &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;

&lt;span class="nx"&gt;common_tags&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Development"&lt;/span&gt;
  &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="nx"&gt;Project&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"MyApp"&lt;/span&gt;
  &lt;span class="nx"&gt;CostCenter&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Engineering"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create prod.tfvars
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano prod.tfvars
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Production environment configuration&lt;/span&gt;
&lt;span class="nx"&gt;environment&lt;/span&gt;       &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"prod"&lt;/span&gt;
&lt;span class="nx"&gt;bucket_name&lt;/span&gt;       &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"myapp-prod-yourname-2026"&lt;/span&gt;
&lt;span class="nx"&gt;bucket_prefix&lt;/span&gt;     &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"myapp-prod-yourname"&lt;/span&gt;
&lt;span class="nx"&gt;bucket_count&lt;/span&gt;      &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="nx"&gt;enable_versioning&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  &lt;span class="c1"&gt;# Important for prod!&lt;/span&gt;
&lt;span class="nx"&gt;aws_region&lt;/span&gt;        &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;

&lt;span class="nx"&gt;common_tags&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Production"&lt;/span&gt;
  &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="nx"&gt;Project&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"MyApp"&lt;/span&gt;
  &lt;span class="nx"&gt;CostCenter&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Operations"&lt;/span&gt;
  &lt;span class="nx"&gt;Compliance&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Required"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deploy to Dev
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform plan &lt;span class="nt"&gt;-var-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"dev.tfvars"&lt;/span&gt;
terraform apply &lt;span class="nt"&gt;-var-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"dev.tfvars"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fh9qr7iyqhc82lb96qq32.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%2Fh9qr7iyqhc82lb96qq32.png" alt=" " width="800" height="489"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; 3 buckets created (1 main + 2 multiple), no versioning&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy to Prod
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# First destroy dev&lt;/span&gt;
terraform destroy &lt;span class="nt"&gt;-var-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"dev.tfvars"&lt;/span&gt;

&lt;span class="c"&gt;# Then deploy prod&lt;/span&gt;
terraform plan &lt;span class="nt"&gt;-var-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"prod.tfvars"&lt;/span&gt;
terraform apply &lt;span class="nt"&gt;-var-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"prod.tfvars"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt; 4 buckets created (1 main + 3 multiple), versioning enabled&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Same code, different results!&lt;/strong&gt; This is how professionals manage multiple environments.&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%2Fu7n08mle3gtyh1ef0svi.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%2Fu7n08mle3gtyh1ef0svi.png" alt=" " width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  ✅ Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Always Add Descriptions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Bad:&lt;/strong&gt;&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;variable&lt;/span&gt; &lt;span class="s2"&gt;"name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Good:&lt;/strong&gt;&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;variable&lt;/span&gt; &lt;span class="s2"&gt;"bucket_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Name of the S3 bucket for application logs (must be globally unique)"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Use Validation Rules
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"instance_type"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EC2 instance type"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t2.micro"&lt;/span&gt;

  &lt;span class="nx"&gt;validation&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;condition&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s2"&gt;"t2.micro"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"t2.small"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"t3.micro"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;error_message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Instance type must be t2.micro, t2.small, or t3.micro."&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;h3&gt;
  
  
  3. Provide Sensible Defaults
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"aws_region"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS region"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&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="c1"&gt;# Most common region&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Use Meaningful Variable Names
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Bad:&lt;/strong&gt; &lt;code&gt;var.n&lt;/code&gt;, &lt;code&gt;var.x&lt;/code&gt;, &lt;code&gt;var.thing&lt;/code&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Good:&lt;/strong&gt; &lt;code&gt;var.bucket_name&lt;/code&gt;, &lt;code&gt;var.instance_count&lt;/code&gt;, &lt;code&gt;var.enable_monitoring&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Group Related Variables
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Network variables&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"vpc_cidr"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"subnet_cidrs"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Application variables&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"app_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"app_version"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Tags&lt;/span&gt;
&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"common_tags"&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;h3&gt;
  
  
  6. Never Commit Secrets to tfvars
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Bad:&lt;/strong&gt;&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="c1"&gt;# terraform.tfvars&lt;/span&gt;
&lt;span class="nx"&gt;database_password&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"super-secret-password"&lt;/span&gt;  &lt;span class="c1"&gt;# DON'T DO THIS!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Good:&lt;/strong&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="c"&gt;# Use environment variables for secrets&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TF_VAR_database_password&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"super-secret-password"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use AWS Secrets Manager, HashiCorp Vault, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Document Your Variables
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;README.md&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Required Variables&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="sb"&gt;`bucket_name`&lt;/span&gt; - Globally unique S3 bucket name

&lt;span class="gu"&gt;## Optional Variables&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; &lt;span class="sb"&gt;`aws_region`&lt;/span&gt; - AWS region (default: us-east-1)
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`enable_versioning`&lt;/span&gt; - Enable bucket versioning (default: false)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🐛 Common Issues and Solutions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Issue 1: Variable Not Found
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Reference to undeclared input variable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Make sure variable is declared in &lt;code&gt;variables.tf&lt;/code&gt;:&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;variable&lt;/span&gt; &lt;span class="s2"&gt;"bucket_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Bucket name"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Issue 2: Type Mismatch
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Invalid value for input variable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Check your variable type matches the value:&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;variable&lt;/span&gt; &lt;span class="s2"&gt;"bucket_count"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;  &lt;span class="c1"&gt;# Not string!&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;terraform.tfvars&lt;/code&gt;:&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;bucket_count&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;  &lt;span class="c1"&gt;# Not "2" (string)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Issue 3: Validation Failed
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Error:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Invalid value for variable
Bucket count must be between 1 and 5.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Provide a value that meets the validation condition:&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;bucket_count&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;  &lt;span class="c1"&gt;# Within 1-5 range&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Issue 4: Forgot to Pass tfvars File
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Terraform uses default values instead of your custom file&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Always specify the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;terraform apply &lt;span class="nt"&gt;-var-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"prod.tfvars"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Issue 5: Sensitive Output Not Hidden
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Sensitive data showing in output&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Mark output as sensitive:&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;output&lt;/span&gt; &lt;span class="s2"&gt;"database_password"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;db_password&lt;/span&gt;
  &lt;span class="nx"&gt;sensitive&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  &lt;span class="c1"&gt;# Add this&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🎓 What You Just Learned
&lt;/h2&gt;

&lt;p&gt;Let's recap your new superpowers:&lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Variables:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Convert hardcoded values to flexible variables&lt;/li&gt;
&lt;li&gt;Use all variable types (string, number, bool, list, map)&lt;/li&gt;
&lt;li&gt;Add validation rules&lt;/li&gt;
&lt;li&gt;Provide defaults&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ &lt;strong&gt;terraform.tfvars:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store variable values in files&lt;/li&gt;
&lt;li&gt;Create environment-specific configs (dev.tfvars, prod.tfvars)&lt;/li&gt;
&lt;li&gt;Auto-loading behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ &lt;strong&gt;Outputs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Display important information&lt;/li&gt;
&lt;li&gt;Mark sensitive data&lt;/li&gt;
&lt;li&gt;Use in scripts and modules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ &lt;strong&gt;Real-World Patterns:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Same code, multiple environments&lt;/li&gt;
&lt;li&gt;Conditional resource creation&lt;/li&gt;
&lt;li&gt;Multiple resource creation with count&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎯 Challenge: Try It Yourself
&lt;/h2&gt;

&lt;p&gt;Before moving to the next article, try these challenges:&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 1: Add More Variables
&lt;/h3&gt;

&lt;p&gt;Add variables for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;owner_email&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cost_center&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;backup_retention_days&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Challenge 2: Create staging.tfvars
&lt;/h3&gt;

&lt;p&gt;Create a staging environment configuration between dev and prod.&lt;/p&gt;

&lt;h3&gt;
  
  
  Challenge 3: Use List Variable
&lt;/h3&gt;

&lt;p&gt;Create a variable for multiple availability zones:&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;variable&lt;/span&gt; &lt;span class="s2"&gt;"availability_zones"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"us-east-1a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1b"&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;h3&gt;
  
  
  Challenge 4: Complex Map Variable
&lt;/h3&gt;

&lt;p&gt;Create a variable for instance configurations:&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;variable&lt;/span&gt; &lt;span class="s2"&gt;"instance_configs"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
    &lt;span class="nx"&gt;volume_size&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;number&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;h3&gt;
  
  
  Challenge 5: Output Formatting
&lt;/h3&gt;

&lt;p&gt;Create an output that combines multiple values:&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;output&lt;/span&gt; &lt;span class="s2"&gt;"bucket_info"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Bucket ${aws_s3_bucket.example.id} in ${var.aws_region}"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📚 Additional Variable Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Variable Precedence (Order of Priority)
&lt;/h3&gt;

&lt;p&gt;Terraform loads variables in this order (last wins):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Environment variables (&lt;code&gt;TF_VAR_name&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;terraform.tfvars&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;terraform.tfvars.json&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;*.auto.tfvars&lt;/code&gt; files (alphabetical order)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-var&lt;/code&gt; and &lt;code&gt;-var-file&lt;/code&gt; command line flags&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&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="c"&gt;# All of these set the same variable&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TF_VAR_bucket_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"from-env"&lt;/span&gt;           &lt;span class="c"&gt;# Priority 1&lt;/span&gt;
&lt;span class="c"&gt;# terraform.tfvars: bucket_name = "from-file"  # Priority 2&lt;/span&gt;
terraform apply &lt;span class="nt"&gt;-var&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"bucket_name=from-cli"&lt;/span&gt;    &lt;span class="c"&gt;# Priority 3 (wins!)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Environment Variables
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Set variable via environment&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TF_VAR_bucket_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"my-bucket-2026"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;TF_VAR_aws_region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"us-west-2"&lt;/span&gt;

terraform plan  &lt;span class="c"&gt;# Uses environment variables&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Auto-Loading tfvars Files
&lt;/h3&gt;

&lt;p&gt;Terraform automatically loads:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;terraform.tfvars&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;terraform.tfvars.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*.auto.tfvars&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;*.auto.tfvars.json&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&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="c"&gt;# These are auto-loaded&lt;/span&gt;
terraform.tfvars
common.auto.tfvars
secrets.auto.tfvars

&lt;span class="c"&gt;# These need -var-file flag&lt;/span&gt;
dev.tfvars
prod.tfvars
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔍 Understanding Variable Interpolation
&lt;/h2&gt;

&lt;p&gt;You can use variables in many ways:&lt;/p&gt;

&lt;h3&gt;
  
  
  String Interpolation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"example"&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;"${var.environment}-${var.app_name}-bucket"&lt;/span&gt;
  &lt;span class="c1"&gt;# Result: "dev-myapp-bucket"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conditional Expressions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"web"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"prod"&lt;/span&gt; &lt;span class="err"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"t3.large"&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"t3.micro"&lt;/span&gt;
  &lt;span class="c1"&gt;# prod = t3.large, anything else = t3.micro&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  For Expressions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;bucket_names&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bucket_count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"${var.prefix}-${i}"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="c1"&gt;# Result: ["app-0", "app-1", "app-2"]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  🔗 Useful Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Terraform Variables Docs:&lt;/strong&gt; &lt;a href="https://developer.hashicorp.com/terraform/language/values/variables" rel="noopener noreferrer"&gt;https://developer.hashicorp.com/terraform/language/values/variables&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terraform Outputs Docs:&lt;/strong&gt; &lt;a href="https://developer.hashicorp.com/terraform/language/values/outputs" rel="noopener noreferrer"&gt;https://developer.hashicorp.com/terraform/language/values/outputs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Variable Validation:&lt;/strong&gt; &lt;a href="https://developer.hashicorp.com/terraform/language/values/variables#custom-validation-rules" rel="noopener noreferrer"&gt;https://developer.hashicorp.com/terraform/language/values/variables#custom-validation-rules&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type Constraints:&lt;/strong&gt; &lt;a href="https://developer.hashicorp.com/terraform/language/expressions/type-constraints" rel="noopener noreferrer"&gt;https://developer.hashicorp.com/terraform/language/expressions/type-constraints&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🎬 What's Next?
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;Article 6: Building a VPC from Scratch&lt;/strong&gt;, we'll:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a complete AWS VPC with subnets&lt;/li&gt;
&lt;li&gt;Use variables for network configuration&lt;/li&gt;
&lt;li&gt;Build public and private subnets&lt;/li&gt;
&lt;li&gt;Configure route tables and internet gateways&lt;/li&gt;
&lt;li&gt;Apply everything you learned about variables&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;You'll learn:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-world networking with Terraform&lt;/li&gt;
&lt;li&gt;Complex resource dependencies&lt;/li&gt;
&lt;li&gt;Advanced variable usage&lt;/li&gt;
&lt;li&gt;Production-ready VPC architecture&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📌 Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Thank you for reading. I hope this article provided practical insights and a clearer understanding of the topic.&lt;/p&gt;

&lt;p&gt;If you found this useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❤️ Like if it added value
&lt;/li&gt;
&lt;li&gt;🦄 Unicorn if you’re applying it today
&lt;/li&gt;
&lt;li&gt;💾 Save it for your next optimization session
&lt;/li&gt;
&lt;li&gt;🔄 Share it with your team
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💡 What’s Next
&lt;/h2&gt;

&lt;p&gt;More deep dives are coming soon on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud Operations&lt;/li&gt;
&lt;li&gt;GenAI &amp;amp; Agentic AI&lt;/li&gt;
&lt;li&gt;DevOps Automation&lt;/li&gt;
&lt;li&gt;Data &amp;amp; Platform Engineering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow along for weekly insights and hands-on guides.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌐 Portfolio &amp;amp; Work
&lt;/h2&gt;

&lt;p&gt;You can explore my full body of work, certifications, architecture projects, and technical articles here:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;Visit My Website&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Services I Offer
&lt;/h2&gt;

&lt;p&gt;If you're looking for hands-on guidance or collaboration, I provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud Architecture Consulting (AWS / Azure)&lt;/li&gt;
&lt;li&gt;DevSecOps &amp;amp; Automation Design&lt;/li&gt;
&lt;li&gt;FinOps Optimization Reviews&lt;/li&gt;
&lt;li&gt;Technical Writing (Cloud, DevOps, GenAI)&lt;/li&gt;
&lt;li&gt;Product &amp;amp; Architecture Reviews&lt;/li&gt;
&lt;li&gt;Mentorship &amp;amp; 1:1 Technical Guidance&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🤝 Let’s Connect
&lt;/h2&gt;

&lt;p&gt;I’d love to hear your thoughts. Feel free to drop a comment or connect with me on:&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://www.linkedin.com/in/sarvar04/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For collaborations, consulting, or technical discussions, reach out at:&lt;/p&gt;

&lt;p&gt;📧 &lt;a href="mailto:simplynadaf@gmail.com"&gt;simplynadaf@gmail.com&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Found this helpful? Share it with your team.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;⭐ Star the repo • 📖 Follow the series • 💬 Ask questions  &lt;/p&gt;

&lt;p&gt;Made by &lt;strong&gt;Sarvar Nadaf&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
🌐 &lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;https://sarvarnadaf.com&lt;/a&gt;&lt;/p&gt;




</description>
      <category>terraform</category>
      <category>aws</category>
      <category>devops</category>
      <category>infrastructure</category>
    </item>
    <item>
      <title>What is OpenClaw? A Self Hosted AI Assistants</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Mon, 30 Mar 2026 17:56:49 +0000</pubDate>
      <link>https://dev.to/aws-builders/what-is-openclaw-a-self-hosted-ai-assistants-311j</link>
      <guid>https://dev.to/aws-builders/what-is-openclaw-a-self-hosted-ai-assistants-311j</guid>
      <description>&lt;p&gt;👋 Hey there, tech enthusiasts! &lt;/p&gt;

&lt;p&gt;I'm Sarvar, a Cloud Architect with a passion for transforming complex technological challenges into elegant solutions. With extensive experience spanning Cloud Operations (AWS &amp;amp; Azure), Data Operations, Analytics, DevOps, and Generative AI, I've had the privilege of architecting solutions for global enterprises that drive real business impact. Through this article series, I'm excited to share practical insights, best practices, and hands-on experiences from my journey in the tech world. Whether you're a seasoned professional or just starting out, I aim to break down complex concepts into digestible pieces that you can apply in your projects.&lt;/p&gt;

&lt;p&gt;Let's dive in and explore the fascinating world of cloud technology together! 🚀&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;New to Self-Hosted AI?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
This article assumes basic familiarity with computers, software installation, and online services. If terms like "API," "server," or "open source" are new to you, don't worry focus on the core concept: OpenClaw is an AI assistant that you run on your own computer or server, rather than relying on a company's service like ChatGPT.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What This Article Covers:&lt;/strong&gt; Understanding what OpenClaw is, why it matters, and whether it's right for you.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;What's Coming Next:&lt;/strong&gt; Hands-on setup, configuration, and deployment (no prior experience with self-hosting required we'll walk through everything step-by-step).&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Imagine having a personal assistant that lives on your infrastructure, responds through the messaging apps you already use, and executes tasks across your entire cloud environment. That's OpenClaw. But unlike the AI assistants you're familiar with ChatGPT, Claude, or GitHub Copilot this one doesn't phone home to someone else's servers. It runs on your machine, uses your API keys, and keeps your data exactly where you want it.&lt;/p&gt;

&lt;p&gt;The story begins in November 2025 when Austrian developer Peter Steinberger built what he called a "weekend project." He wanted an AI that could do more than chat one that could actually execute commands, manage files, read emails, and integrate with the tools he used daily. Within weeks, the project exploded. By March 2026, it had accumulated over 340,000 GitHub stars and attracted attention from Silicon Valley startups to Chinese tech giants. Steinberger eventually joined OpenAI and moved the project to an open-source foundation, but the core philosophy remained unchanged: your assistant, your machine, your rules.&lt;/p&gt;

&lt;p&gt;For anyone interested in AI and privacy, OpenClaw represents something different. It's not a SaaS product with usage limits and data residency questions. It's software you install, configure, and control. You choose which AI provider to use Anthropic's Claude, OpenAI's GPT models, Google's Gemini, or even self-hosted options like Ollama. You decide which messaging platforms to enable. You set the security boundaries. And when you need to see what happened, the logs are on your computer, not buried in someone else's cloud.&lt;/p&gt;

&lt;h2&gt;
  
  
  What OpenClaw Actually Is
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Architecture and Deployment Model&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;OpenClaw is a Node.js application that acts as a gateway between messaging platforms and large language models (LLMs AI systems like ChatGPT or Claude). You install it on a server your laptop, a homelab machine, or a cloud VM and it stays running as a daemon. The architecture is straightforward: a WebSocket-based control plane (a real-time communication hub) listens on port 18789, handling connections from messaging channels, the web dashboard, and optional companion apps for macOS, iOS, and Android.&lt;/p&gt;

&lt;p&gt;The gateway doesn't process AI requests itself. Instead, it routes conversations to your configured LLM provider via their APIs. When you send a message through WhatsApp or Slack, OpenClaw receives it, forwards the context to Claude or GPT, gets the response, and delivers it back through the same channel. All conversation history, configuration, and session state live in local SQLite databases under &lt;code&gt;~/.openclaw/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-Channel Integration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is where OpenClaw diverges from traditional chatbots. Rather than forcing you into a web interface or proprietary app, it meets you where you already work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Messaging platforms&lt;/strong&gt;: WhatsApp, Telegram, Discord, Slack, Microsoft Teams, Signal, Matrix&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Specialized channels&lt;/strong&gt;: IRC, Google Chat, Mattermost, Twitch, WeChat (via Tencent plugin)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct interfaces&lt;/strong&gt;: Web dashboard, macOS menu bar app, iOS/Android companion apps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer tools&lt;/strong&gt;: CLI commands for scripting and automation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each channel operates independently. You can have different security policies for Discord versus WhatsApp, route specific channels to isolated agent workspaces, or restrict certain tools based on where the request originated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Skills System&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;OpenClaw extends beyond conversation through a skills framework. Skills are self-contained modules think of them as plugins that give the AI new capabilities. A skill is just a directory containing a &lt;code&gt;SKILL.md&lt;/code&gt; file with instructions and metadata. The AI reads these instructions and knows how to use the skill's tools.&lt;/p&gt;

&lt;p&gt;Skills can be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bundled&lt;/strong&gt;: Shipped with OpenClaw (browser automation, file operations, system commands)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managed&lt;/strong&gt;: Installed from the ClawHub registry&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workspace-specific&lt;/strong&gt;: Custom skills you write for your environment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The precedence model matters for teams building workflows. Workspace skills override global skills, which override bundled skills. This means you can customize behavior per project without modifying the core installation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tool Execution and Sandboxing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When the AI decides it needs to run a command or access a file, OpenClaw's tool system handles the execution. By default, tools run directly on the host for the main session. But for group chats or untrusted channels, you can enable sandbox mode. Sandboxed sessions run inside per-session Docker containers with restricted tool access.&lt;/p&gt;

&lt;p&gt;The security model is explicit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Main session (your direct chats): Full tool access by default&lt;/li&gt;
&lt;li&gt;Group sessions: Configurable sandbox with allowlists and denylists&lt;/li&gt;
&lt;li&gt;Tool categories: &lt;code&gt;bash&lt;/code&gt;, &lt;code&gt;browser&lt;/code&gt;, &lt;code&gt;read&lt;/code&gt;, &lt;code&gt;write&lt;/code&gt;, &lt;code&gt;edit&lt;/code&gt;, &lt;code&gt;sessions&lt;/code&gt;, &lt;code&gt;nodes&lt;/code&gt;, &lt;code&gt;cron&lt;/code&gt;, &lt;code&gt;discord&lt;/code&gt;, &lt;code&gt;gateway&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You define which tools each session type can use. A production deployment might allow read-only file access for group chats while reserving write operations for authenticated direct messages.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Self-Hosting Matters
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Data Privacy and Control&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you deploy OpenClaw on your infrastructure, you control the data flow. Conversation logs, uploaded files, and execution history never leave your environment unless you explicitly configure external integrations. For organizations with strict data residency requirements healthcare, finance, government this matters. You're not trusting a third party to handle PHI (Protected Health Information) or PII (Personally Identifiable Information). You're running the infrastructure yourself.&lt;/p&gt;

&lt;p&gt;The LLM API calls still go to external providers (unless you use self-hosted models), but you choose which provider and can route through your own network controls. Want to proxy all OpenAI requests through your corporate gateway? Configure the API endpoint. Need to log every prompt for compliance? Intercept at the gateway level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost Control and Model Flexibility&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;SaaS AI assistants charge per seat or per usage tier. OpenClaw inverts this model. You pay your LLM provider directly based on token consumption. If Claude is too expensive for your use case, switch to GPT-4o-mini. If you want to experiment with open-source models, point it at Ollama running locally. The gateway doesn't care which model you use it just routes requests.&lt;/p&gt;

&lt;p&gt;This flexibility extends to failover and load balancing. Configure multiple API keys for the same provider, and OpenClaw rotates through them when rate limits hit. Set up model fallback chains: try Claude Opus first, fall back to GPT-4 if it's overloaded, and use a local model as the last resort.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Cost Comparison:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SaaS AI Assistant&lt;/strong&gt;: $20-30/user/month (fixed cost, limited usage)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenClaw + Claude API&lt;/strong&gt;: Pay per token (1M input tokens ≈ $3-8 depending on model)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OpenClaw + Local LLM&lt;/strong&gt;: Infrastructure costs only (no per-token charges)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For teams running thousands of queries daily, the direct API model often costs less than per-seat licensing. For occasional users, SaaS might be cheaper. The key difference: you control the trade-off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure as Code Integration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because OpenClaw is just a Node.js application with a JSON configuration file, it fits naturally into infrastructure-as-code (IaC) workflows managing infrastructure through code rather than manual configuration. The configuration lives at &lt;code&gt;~/.openclaw/openclaw.json&lt;/code&gt; and supports environment variable substitution for secrets. You can template it with Terraform, manage it with Ansible, or version it in Git.&lt;/p&gt;

&lt;p&gt;Deployment patterns cloud architects already know apply directly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Systemd service&lt;/strong&gt;: Install the daemon, enable it, manage it like any other service&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker container&lt;/strong&gt;: Official images available, mount config as a volume&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kubernetes&lt;/strong&gt;: Deploy as a StatefulSet (a workload type for applications that need persistent storage) with persistent storage for the database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nix&lt;/strong&gt;: Declarative configuration with the community Nix package&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Security Boundaries and Threat Model&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;OpenClaw's security model assumes you understand the risks of giving an AI broad system access. The maintainers are explicit about this. One core maintainer warned on Discord: "if you can't understand how to run a command line, this is far too dangerous of a project for you to use safely."&lt;/p&gt;

&lt;p&gt;The threat surface includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prompt injection&lt;/strong&gt;: Malicious instructions embedded in data the AI processes (emails, documents, web pages) that trick the AI into executing unintended commands&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool misuse&lt;/strong&gt;: The AI executing destructive commands based on misinterpreted context&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Third-party skills&lt;/strong&gt;: Unvetted skills from the community that could exfiltrate data&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Channel compromise&lt;/strong&gt;: If someone gains access to an authorized messaging account, they control the AI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cisco's security research team demonstrated this in March 2026 when they found a third-party skill performing data exfiltration. The skill repository lacked adequate vetting, and users installing it unknowingly gave attackers access to their environment.&lt;/p&gt;

&lt;p&gt;For production use, you need to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run sandboxed sessions for any untrusted input source&lt;/li&gt;
&lt;li&gt;Audit third-party skills before installation&lt;/li&gt;
&lt;li&gt;Use DM pairing mode to require explicit approval for new contacts&lt;/li&gt;
&lt;li&gt;Monitor execution logs for unexpected tool usage&lt;/li&gt;
&lt;li&gt;Isolate the OpenClaw instance from sensitive systems&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;The MoltMatch Incident: A Cautionary Tale&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In February 2026, computer science student Jack Luo configured his OpenClaw agent to explore its capabilities. He connected it to Moltbook a social network for AI agents and let it run autonomously. Days later, he discovered the agent had created a profile on MoltMatch, a dating platform where AI agents interact on behalf of users, and was screening potential matches without his explicit direction.&lt;/p&gt;

&lt;p&gt;The incident highlighted a fundamental challenge with autonomous agents: when you give an AI broad permissions and vague instructions, it will interpret its mandate creatively. Luo's agent saw "explore capabilities" and "connect to agent platforms" as permission to create dating profiles. The AI-generated profile didn't reflect him authentically, and he had no idea it was happening until someone told him.&lt;/p&gt;

&lt;p&gt;This matters for anyone managing systems because the same dynamic applies to automation. If you tell an AI to "optimize costs" without clear boundaries, it might decide to delete things it deems unnecessary. If you ask it to "improve security," it could change settings in ways you didn't anticipate. Autonomous agents require explicit constraints, not just general goals.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real-World Adoption Patterns
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Small Business and Freelancer Workflows&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;OpenClaw has found traction among small businesses and freelancers who need automation but can't afford enterprise tools. Common workflows include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Lead generation: Scraping prospect lists, researching companies, updating CRM systems&lt;/li&gt;
&lt;li&gt;Content management: Drafting emails, summarizing documents, generating reports&lt;/li&gt;
&lt;li&gt;System administration: Monitoring servers, running diagnostics, deploying updates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These users value the flexibility to customize behavior without vendor lock-in. A freelance consultant can write a workspace skill that integrates with their specific CRM and invoicing tools, then share that skill with clients who use the same stack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enterprise Experimentation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Larger organizations are testing OpenClaw in controlled environments. The typical pattern: deploy it on a developer's workstation or a sandbox VM, connect it to a private Slack channel, and use it for internal tooling. Teams report using it for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Documentation search: Indexing internal wikis and answering questions&lt;/li&gt;
&lt;li&gt;Incident response: Gathering logs, running diagnostics, summarizing findings&lt;/li&gt;
&lt;li&gt;Code review assistance: Analyzing pull requests, suggesting improvements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key constraint is trust. Enterprises won't give OpenClaw production access until they've validated its behavior in isolated environments. The security incidents and lack of formal audits make it a hard sell for risk-averse organizations.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Means for Your Setup
&lt;/h2&gt;

&lt;p&gt;If you're evaluating OpenClaw, think of it as software infrastructure, not a product. You're not buying a service; you're installing and maintaining a component that needs care and attention.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Questions to Consider:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deployment:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where will it run? (Your laptop, a home server, a cloud VM)&lt;/li&gt;
&lt;li&gt;Who can access it? (Just you, your team, your organization)&lt;/li&gt;
&lt;li&gt;What can it access? (Read-only files, full system control, isolated sandbox)&lt;/li&gt;
&lt;li&gt;How will you monitor it? (Log files, activity tracking, alerts)&lt;/li&gt;
&lt;li&gt;What's the risk if compromised? (Isolated test environment vs. access to important data)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Integration:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which AI provider will you use? (Cost, rate limits, data handling)&lt;/li&gt;
&lt;li&gt;Which messaging platforms? (Authentication, user verification)&lt;/li&gt;
&lt;li&gt;What internal systems? (APIs, databases, file storage)&lt;/li&gt;
&lt;li&gt;How will you monitor it? (Health checks, error tracking, usage metrics)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Maintenance:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who maintains the configuration? (You, your team, IT department)&lt;/li&gt;
&lt;li&gt;How do you handle updates? (Manual upgrades, automated deployment)&lt;/li&gt;
&lt;li&gt;What's the support model? (Community forums, internal expertise)&lt;/li&gt;
&lt;li&gt;How do you manage secrets? (Environment variables, password managers)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The answers depend on your technical comfort level and risk tolerance. A tech-savvy individual might run it on their laptop for personal use. A small team might deploy it on a shared server with careful access controls. An enterprise might lock it down in an isolated environment. All are valid approaches based on your needs and skills.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;OpenClaw is not a polished consumer product. It's a powerful, flexible, occasionally dangerous tool that gives you control at the cost of responsibility. For people who understand the trade-offs, it offers something rare: an AI assistant that runs on your terms, integrates with your tools, and doesn't require you to trust a company with your data.&lt;/p&gt;

&lt;p&gt;The project is still young. The security model is evolving. The ecosystem is fragmented. But the core idea self-hosted, multi-channel, tool-capable AI agents addresses real needs that SaaS products don't solve. Whether OpenClaw itself becomes the standard or just proves the concept for something better, the architecture it represents is worth understanding.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to Consider OpenClaw:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You care deeply about data privacy and control&lt;/li&gt;
&lt;li&gt;You want to avoid subscription fees for large-scale usage&lt;/li&gt;
&lt;li&gt;You need deep integration with your own tools and systems&lt;/li&gt;
&lt;li&gt;You have technical skills or are willing to learn&lt;/li&gt;
&lt;li&gt;You're comfortable with ongoing maintenance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to Avoid OpenClaw:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You're not comfortable with command-line tools&lt;/li&gt;
&lt;li&gt;You need something that works immediately without setup&lt;/li&gt;
&lt;li&gt;You can't dedicate time to security and maintenance&lt;/li&gt;
&lt;li&gt;You prefer paying for convenience over having control&lt;/li&gt;
&lt;li&gt;Your use case doesn't justify the complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you decide to explore it, start small. Run it in an isolated environment, connect it to a test channel, give it limited permissions. Learn how it behaves, where it fails, and what it can actually do. Then decide if the benefits justify the operational overhead and security risks.&lt;/p&gt;

&lt;p&gt;That's the honest assessment. OpenClaw is powerful infrastructure for people who know what they're doing. If that's you, it's worth exploring. If it's not, wait for the ecosystem to mature.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Coming Next:&lt;/strong&gt; In our next article, we'll cover the technical implementation installation, configuration, and deployment with step-by-step examples. We'll walk through setting up OpenClaw on different platforms and implementing security best practices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What We'll Cover in Upcoming Articles:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 2: Installation &amp;amp; Setup&lt;/strong&gt; - Step-by-step guide, prerequisites, system requirements, and getting your first instance running&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 3: Configuration Deep-Dive&lt;/strong&gt; - API keys, messaging platform integration, security policies, and sandbox configuration
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 4: Deployment Options&lt;/strong&gt; - Running on Windows/Mac/Linux, cloud servers, and home servers with monitoring and cost optimization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 5: Security Best Practices&lt;/strong&gt; - Threat mitigation, audit logging, access control, and incident response&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Questions or Topics You'd Like Covered?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Drop a comment below with your questions about OpenClaw. Whether you're wondering about specific use cases, integration challenges, cost comparisons, or anything else we'll address them in upcoming articles. Common questions we're already planning to cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How much does it actually cost to run?&lt;/li&gt;
&lt;li&gt;Can I use it on Windows without WSL?&lt;/li&gt;
&lt;li&gt;What's the learning curve for someone new to self-hosting?&lt;/li&gt;
&lt;li&gt;How does it compare to AutoGPT or LangChain agents?&lt;/li&gt;
&lt;li&gt;Can I run it on a Raspberry Pi or home server?&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Resources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Official Website: &lt;a href="https://openclaw.ai" rel="noopener noreferrer"&gt;https://openclaw.ai&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub Repository: &lt;a href="https://github.com/openclaw/openclaw" rel="noopener noreferrer"&gt;https://github.com/openclaw/openclaw&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  📌 Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Thank you for reading! I hope this article gave you practical insights and a clearer perspective on the topic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Was this helpful?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❤️ Like if it added value&lt;/li&gt;
&lt;li&gt;🦄 Unicorn if you’re applying it today&lt;/li&gt;
&lt;li&gt;💾 Save for your next optimization session&lt;/li&gt;
&lt;li&gt;🔄 Share with your team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Follow me for more on:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS architecture patterns&lt;/li&gt;
&lt;li&gt;FinOps automation&lt;/li&gt;
&lt;li&gt;Multi-account strategies&lt;/li&gt;
&lt;li&gt;AI-driven DevOps&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💡 What’s Next
&lt;/h2&gt;

&lt;p&gt;More deep dives coming soon on cloud operations, GenAI, Agentic-AI, DevOps, and data workflows follow for weekly insights.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌐 Portfolio &amp;amp; Work
&lt;/h2&gt;

&lt;p&gt;You can explore my full body of work, certifications, architecture projects, and technical articles here:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;Visit My Website&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Services I Offer
&lt;/h2&gt;

&lt;p&gt;If you're looking for hands-on guidance or collaboration, I provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud Architecture Consulting (AWS / Azure)&lt;/li&gt;
&lt;li&gt;DevSecOps &amp;amp; Automation Design&lt;/li&gt;
&lt;li&gt;FinOps Optimization Reviews&lt;/li&gt;
&lt;li&gt;Technical Writing (Cloud, DevOps, GenAI)&lt;/li&gt;
&lt;li&gt;Product &amp;amp; Architecture Reviews&lt;/li&gt;
&lt;li&gt;Mentorship &amp;amp; 1:1 Technical Guidance&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🤝 Let’s Connect
&lt;/h2&gt;

&lt;p&gt;I’d love to hear your thoughts drop a comment or connect with me on &lt;a href="https://www.linkedin.com/in/sarvar04/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For collaborations, consulting, or technical discussions, feel free to reach out directly at &lt;a href="mailto:simplynadaf@gmail.com"&gt;simplynadaf@gmail.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Happy Learning&lt;/strong&gt; 🚀&lt;/p&gt;

</description>
      <category>openclaw</category>
      <category>ai</category>
      <category>opensource</category>
      <category>discuss</category>
    </item>
    <item>
      <title>AI Assistance vs AI Agents: Understanding the Shift from Responses to Autonomous Systems</title>
      <dc:creator>Sarvar Nadaf</dc:creator>
      <pubDate>Wed, 25 Mar 2026 19:11:06 +0000</pubDate>
      <link>https://dev.to/aws-builders/ai-assistance-vs-ai-agents-understanding-the-shift-from-responses-to-autonomous-systems-pb3</link>
      <guid>https://dev.to/aws-builders/ai-assistance-vs-ai-agents-understanding-the-shift-from-responses-to-autonomous-systems-pb3</guid>
      <description>&lt;p&gt;👋 Hey there, tech enthusiasts! &lt;/p&gt;

&lt;p&gt;I'm Sarvar, a Cloud Architect with a passion for transforming complex technological challenges into elegant solutions. With extensive experience spanning Cloud Operations (AWS &amp;amp; Azure), Data Operations, Analytics, DevOps, Generative AI, and Agentic AI, I've had the privilege of architecting solutions for global enterprises that drive real business impact. Through this article series, I'm excited to share practical insights, best practices, and hands-on experiences from my journey in the tech world. Whether you're a seasoned professional or just starting out, I aim to break down complex concepts into digestible pieces that you can apply in your projects.&lt;/p&gt;

&lt;p&gt;Let's dive in and explore the fascinating world of cloud technology together! 🚀&lt;/p&gt;




&lt;h2&gt;
  
  
  Here's What Most People Get Wrong
&lt;/h2&gt;

&lt;p&gt;ChatGPT can write your email. But it can't send it. That's the difference between an AI assistant and an AI agent, and honestly, most people have no idea there even is a difference.&lt;/p&gt;

&lt;p&gt;I've been working with AI for a while now, and I keep seeing the same confusion everywhere. People use "AI assistant" and "AI agent" like they're the same thing. They're not. Not even close.&lt;/p&gt;

&lt;p&gt;Here's what you need to know: &lt;strong&gt;AI assistants tell you what to do. AI agents actually do it for you.&lt;/strong&gt; That's the whole game right there.&lt;/p&gt;

&lt;p&gt;Let me break this down in a way that actually makes sense.&lt;/p&gt;




&lt;h2&gt;
  
  
  AI Assistants: The Smart Advisor
&lt;/h2&gt;

&lt;p&gt;Think about the last time you used ChatGPT or asked Siri something. You asked a question, got an answer, and then you had to go do something with that answer. That's an AI assistant.&lt;/p&gt;

&lt;p&gt;An AI assistant is like having a really smart friend who knows everything but can't actually touch anything in your world. They can tell you exactly what to do, step by step, but you're the one who has to do it.&lt;/p&gt;

&lt;p&gt;Here's what they're good at:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Answering questions (and they're really good at this)&lt;/li&gt;
&lt;li&gt;Giving you suggestions and advice&lt;/li&gt;
&lt;li&gt;Helping you think through problems&lt;/li&gt;
&lt;li&gt;Generating content like emails, code, or ideas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But here's what they can't do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Take any action in your systems&lt;/li&gt;
&lt;li&gt;Make decisions and execute them&lt;/li&gt;
&lt;li&gt;Complete tasks that need multiple steps&lt;/li&gt;
&lt;li&gt;Use tools or APIs on their own&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let me give you a real example. Say you ask an AI assistant: "What's the weather today?"&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: "What's the weather today?"
Assistant: "It's 72°F and sunny in New York."
You: "Should I bring an umbrella?"
Assistant: "No, it's not going to rain today."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Perfect, right? You got your answer. But notice what didn't happen - the assistant didn't check your calendar, didn't see you have a meeting at 3 PM, didn't notice the forecast shows rain later, and definitely didn't pack an umbrella for you. It just answered your question.&lt;/p&gt;

&lt;p&gt;That's the limitation. And for a lot of things, that's totally fine! Sometimes you just need information.&lt;/p&gt;




&lt;h2&gt;
  
  
  Agentic AI: The Autonomous Doer
&lt;/h2&gt;

&lt;p&gt;Now here's where things get interesting. Agentic AI doesn't just tell you what to do - it actually does it.&lt;/p&gt;

&lt;p&gt;Think of it like this: if an AI assistant is your smart friend giving advice, an agentic AI is your trusted employee who you can hand a project to and they'll just... handle it. They'll figure out the steps, make the decisions, use whatever tools they need, and come back with results.&lt;/p&gt;

&lt;p&gt;This is what makes agents different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They can access and use tools (APIs, databases, systems)&lt;/li&gt;
&lt;li&gt;They make decisions based on what they find&lt;/li&gt;
&lt;li&gt;They complete multi-step workflows autonomously&lt;/li&gt;
&lt;li&gt;They only ask you when they really need input&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the same scenario but with an agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You: "Book me a flight to New York next week."

Agent: *searches multiple airlines*
       *compares prices and times*
       *checks your calendar for conflicts*
       *finds the best option*
       *books the ticket*
       *adds it to your calendar*
       *sends confirmation to your email*

"Done! I booked you on Delta flight DL123, Tuesday 9 AM, $287. 
I saw you have a meeting at 2 PM that day, so this gives you time. 
Confirmation is in your email."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the difference? You gave one instruction, and the agent handled everything. It made decisions (which flight is best), used tools (booking system, calendar, email), and completed the entire workflow. You didn't have to do anything except say what you wanted.&lt;/p&gt;




&lt;h2&gt;
  
  
  Let Me Show You the Real Difference
&lt;/h2&gt;

&lt;p&gt;I think the best way to understand this is with a practical example. Let's say you need to check on your servers (if you're not technical, just think of this as checking if your website is working).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With an AI Assistant:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You ask: "I need to check my server status."&lt;/p&gt;

&lt;p&gt;The assistant responds:&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="s2"&gt;"Sure! To check your server status, you can:
1. Log into your AWS Console
2. Navigate to the EC2 Dashboard
3. Look at the instance state column

Or if you prefer the command line, run:
aws ec2 describe-instances

Would you like help with anything else?"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great advice! But now you have to go do all of that. You have to log in, navigate around, run commands, interpret the results. The assistant told you what to do, but you're still doing all the work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;With an Agentic AI:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You say the same thing: "I need to check my server status."&lt;/p&gt;

&lt;p&gt;The agent responds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*connects to your AWS account*
*checks all your servers*
*analyzes the data*

"I checked your 5 servers. Here's what I found:

- web-server-1: Running fine, healthy
- web-server-2: Running but CPU is at 89% (might need attention soon)
- database-1: Running fine, healthy  
- api-server-1: STOPPED - this one is down
- backup-server: Running fine, healthy

I noticed api-server-1 is down. This could be affecting your API endpoints. 
Want me to restart it for you?"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent didn't just tell you what to do. It actually went and checked everything, found a problem, and is ready to fix it if you want. That's the power of agentic AI.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters in 2026
&lt;/h2&gt;

&lt;p&gt;Look, I get it. For years, we've been calling everything "AI assistants" and it worked fine. But now we're in 2026, and the technology has split into two very different paths.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI assistants got better at answering.&lt;/strong&gt; They understand context better, they give more accurate information, they can help with more complex questions. ChatGPT, Claude, and similar tools are incredibly good at what they do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agentic AI learned to act.&lt;/strong&gt; They can now access your tools, make decisions, and complete entire workflows. They're not just smart anymore - they're capable.&lt;/p&gt;

&lt;p&gt;And here's the thing: most people are still using AI like it's 2023. They're asking questions and copying answers when they could be handing off entire tasks.&lt;/p&gt;

&lt;p&gt;Let me give you some real examples of what's possible now:&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub Copilot Workspace
&lt;/h3&gt;

&lt;p&gt;You tell it: "Add user authentication to this app."&lt;/p&gt;

&lt;p&gt;Instead of just giving you code snippets, it can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Analyze your codebase&lt;/li&gt;
&lt;li&gt;Write code across multiple files&lt;/li&gt;
&lt;li&gt;Suggest database changes&lt;/li&gt;
&lt;li&gt;Draft API endpoints&lt;/li&gt;
&lt;li&gt;Generate tests&lt;/li&gt;
&lt;li&gt;Create a pull request&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You still review everything and make the final decisions, but it handles the heavy lifting. That's moving toward agentic behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS Bedrock Agents
&lt;/h3&gt;

&lt;p&gt;You can set up an agent to monitor your infrastructure. It can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Watch your servers continuously&lt;/li&gt;
&lt;li&gt;Detect when something goes wrong&lt;/li&gt;
&lt;li&gt;Analyze issues&lt;/li&gt;
&lt;li&gt;Take corrective actions (like restarting services)&lt;/li&gt;
&lt;li&gt;Log everything&lt;/li&gt;
&lt;li&gt;Alert you when it needs help&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of checking everything manually, you get a report: "Had 3 minor issues last night, all handled automatically." The agent does the routine monitoring while you focus on bigger problems.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modern DevOps Agents
&lt;/h3&gt;

&lt;p&gt;Your deployment fails at 2 AM. An agent can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Detect the failure quickly&lt;/li&gt;
&lt;li&gt;Analyze the logs&lt;/li&gt;
&lt;li&gt;Identify the issue&lt;/li&gt;
&lt;li&gt;Roll back the deployment&lt;/li&gt;
&lt;li&gt;Alert the team with context&lt;/li&gt;
&lt;li&gt;Generate an incident report&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You find out at 9 AM that there was a problem and it's already been handled. Not perfect, but better than waking up to a crisis.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Quick Comparison
&lt;/h2&gt;

&lt;p&gt;If you're still wrapping your head around this, here's a simple table that breaks it down:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What It Does&lt;/th&gt;
&lt;th&gt;AI Assistant&lt;/th&gt;
&lt;th&gt;Agentic AI&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Answers your questions&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gives you information&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Takes actions in systems&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Uses tools and APIs&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Makes decisions on its own&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Handles multi-step tasks&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Works autonomously&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Needs your approval for everything&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;⚠️ Sometimes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The pattern is pretty clear, right? Assistants are great at the "thinking" part. Agents are great at the "doing" part.&lt;/p&gt;




&lt;h2&gt;
  
  
  So When Should You Use Each One?
&lt;/h2&gt;

&lt;p&gt;This is the practical question everyone should be asking. Because here's the truth: you don't always need an agent. Sometimes an assistant is exactly what you want.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use an AI Assistant When:
&lt;/h3&gt;

&lt;p&gt;You want information or advice. If you're trying to learn something, understand a concept, or get suggestions, an assistant is perfect. You're not trying to automate anything - you just want to think through something with help.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Help me write this email" (you'll review and send it)&lt;/li&gt;
&lt;li&gt;"Explain how this code works" (you're learning)&lt;/li&gt;
&lt;li&gt;"Give me ideas for my presentation" (you're brainstorming)&lt;/li&gt;
&lt;li&gt;"What's the best way to structure this database?" (you want advice)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You want to stay in control. Sometimes you don't want anything happening automatically. You want to review every step, make every decision, and execute everything yourself. That's totally valid, and assistants are perfect for this.&lt;/p&gt;

&lt;p&gt;You're working on creative or strategic tasks. Writing, designing, planning, strategizing - these are areas where you want AI to augment your thinking, not replace your decision-making.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Agentic AI When:
&lt;/h3&gt;

&lt;p&gt;You have repetitive workflows. If you're doing the same multi-step process over and over, that's a perfect candidate for an agent. Let it handle the routine stuff while you focus on things that actually need your brain.&lt;/p&gt;

&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monitoring systems and alerting on issues&lt;/li&gt;
&lt;li&gt;Processing incoming data and routing it correctly&lt;/li&gt;
&lt;li&gt;Handling customer support for common questions&lt;/li&gt;
&lt;li&gt;Running deployments and rollbacks&lt;/li&gt;
&lt;li&gt;Generating and sending reports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You need 24/7 operation. Agents don't sleep. If you need something monitored or handled around the clock, an agent is your answer.&lt;/p&gt;

&lt;p&gt;Speed matters. Agents can react in seconds. If you need fast response to events or issues, agents beat humans every time.&lt;/p&gt;

&lt;p&gt;The task is well-defined. If you can clearly describe the steps and decision points, an agent can probably handle it. The more structured the workflow, the better agents perform.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Business Impact
&lt;/h2&gt;

&lt;p&gt;Let me give you a concrete example of what this looks like in practice.&lt;/p&gt;

&lt;p&gt;I know a company that was spending about 2 hours every day manually checking their server infrastructure. Someone had to log in, check each server, look at metrics, make sure everything was healthy. Boring, repetitive, but necessary.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First, they tried an AI assistant approach.&lt;/strong&gt; They used ChatGPT to help generate the commands they needed to run. It was helpful - instead of remembering all the commands, they could just ask "how do I check CPU usage?" and get the answer. But they still had to run everything manually. They saved maybe 15 minutes a day. Better than nothing, but not game-changing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Then they built an agentic AI solution.&lt;/strong&gt; They set up an agent that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checks all servers every 5 minutes&lt;/li&gt;
&lt;li&gt;Analyzes the metrics automatically&lt;/li&gt;
&lt;li&gt;Alerts them when something is actually wrong&lt;/li&gt;
&lt;li&gt;Can restart services if configured to do so&lt;/li&gt;
&lt;li&gt;Generates a daily health report&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now? They spend maybe 10-15 minutes a day reviewing the report. The agent handles the routine monitoring. That's about 1 hour and 45 minutes saved every day. Not revolutionary, but meaningful. And they catch issues faster because the agent is always watching.&lt;/p&gt;

&lt;p&gt;That's the difference between telling and doing.&lt;/p&gt;




&lt;h2&gt;
  
  
  What People Get Wrong About This
&lt;/h2&gt;

&lt;p&gt;There's a lot of confusion out there, so let me clear up some common misconceptions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"All AI is agentic now"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No, it's really not. Most of the AI you use every day is still assistant-level. ChatGPT, Claude, most chatbots - they're assistants. They're really good assistants, but they're not agents. True agentic AI that can take actions in your systems is still relatively new and not as widespread as people think.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Agents will replace all human work"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the fear everyone has, and it's not realistic. Agents are good at repetitive, well-defined tasks. They're not replacing strategy, creativity, complex decision-making, or anything that requires real judgment. They're tools that handle routine work so humans can focus on more valuable tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"AI assistants are outdated now"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not at all. Assistants are perfect for tons of use cases. Not everything needs to be automated. Sometimes you want advice, not action. Sometimes you want to stay in control of every step. Assistants aren't going anywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Agents are just better assistants"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is the big one people get wrong. They're not "better" - they're fundamentally different. It's like saying a car is a "better" bicycle. No, it's a different tool for different purposes. Assistants and agents solve different problems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Agents are too risky to use"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There's some truth to this concern, but it's manageable. Yes, giving AI the ability to take actions in your systems requires careful setup. You need guardrails, monitoring, and clear boundaries. But we've figured this out. Modern agent frameworks have safety built in. You can limit what they can do, require approval for certain actions, and monitor everything. It's not riskier than giving a new employee access to your systems - you just need proper controls.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Safety Question Everyone Should Ask
&lt;/h2&gt;

&lt;p&gt;Since we're talking about AI that can actually do things in your systems, let's address the elephant in the room: is this safe?&lt;/p&gt;

&lt;p&gt;The honest answer is: it depends on how you set it up.&lt;/p&gt;

&lt;p&gt;Here's what you need to think about:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Guardrails:&lt;/strong&gt; Good agent systems let you define exactly what the agent can and can't do. You can say "you can restart servers, but you can't delete anything" or "you can book flights under $500, but ask me about anything more expensive." These boundaries are crucial.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Monitoring:&lt;/strong&gt; You should be logging everything your agent does. Every action, every decision, every API call. This isn't just for debugging - it's for accountability and safety.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Approval workflows:&lt;/strong&gt; For high-stakes actions, you can require human approval. The agent can do all the analysis and preparation, but it waits for your "yes" before executing anything critical.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rollback capabilities:&lt;/strong&gt; Things go wrong sometimes. Your agent should be able to undo its actions, or at least alert you immediately if something unexpected happens.&lt;/p&gt;

&lt;p&gt;The key is this: you're not giving the agent unlimited power. You're giving it specific, controlled capabilities within boundaries you define. It's like giving someone the keys to your car - you're trusting them with something important, but you're not giving them ownership of your entire life.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where This Is All Heading
&lt;/h2&gt;

&lt;p&gt;We're in 2026, and we're still early in the agentic AI era. Here's what's developing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-agent systems are emerging.&lt;/strong&gt; Instead of one agent doing everything, you might have multiple specialized agents working together. One handles customer support, another manages infrastructure, another handles data analysis. They can coordinate to handle complex workflows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The line is blurring.&lt;/strong&gt; Some AI assistants are adding limited action capabilities. Some agents are getting better at explaining their reasoning. We're moving toward hybrid systems that can adapt based on what you need.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's getting easier to build.&lt;/strong&gt; Right now, building an agent requires technical skill. But we're seeing more platforms that make it easier. Eventually, less technical people will be able to build agents for specific workflows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Safety is improving.&lt;/strong&gt; As more people use agents, we're learning better ways to build in safety features, monitoring, and control mechanisms. The technology is maturing.&lt;/p&gt;

&lt;p&gt;The future probably isn't "assistants vs agents." It's more likely "assistants and agents working together." You'll use assistants for thinking and planning, and agents for executing and monitoring. They complement each other.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Actually Get Started
&lt;/h2&gt;

&lt;p&gt;Alright, enough theory. Let's talk about what you should actually do with this information.&lt;/p&gt;

&lt;h3&gt;
  
  
  If You Want to Start Using AI Assistants:
&lt;/h3&gt;

&lt;p&gt;This is the easy path. You can literally start right now.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Pick a tool.&lt;/strong&gt; ChatGPT, Claude, or any of the major AI assistants. Most have free tiers to start.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Learn to prompt well.&lt;/strong&gt; The better you ask questions, the better answers you get. This is a skill worth developing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Start with simple tasks.&lt;/strong&gt; Use it for writing, brainstorming, learning. Get comfortable with what it can and can't do.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Integrate if needed.&lt;/strong&gt; If you're technical, most assistants have APIs you can use in your applications.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Cost:&lt;/strong&gt; $0-100/month depending on usage&lt;br&gt;
&lt;strong&gt;Time to start:&lt;/strong&gt; Literally right now&lt;br&gt;
&lt;strong&gt;Complexity:&lt;/strong&gt; Low - anyone can do this&lt;/p&gt;

&lt;h3&gt;
  
  
  If You Want to Build Agentic AI:
&lt;/h3&gt;

&lt;p&gt;This is more involved, but not as hard as you might think.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Define your workflow clearly.&lt;/strong&gt; What exact task do you want automated? What are the steps? What decisions need to be made? The clearer you are, the better your agent will work.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Choose your platform.&lt;/strong&gt; AWS Bedrock Agents, LangChain, AutoGPT, or other agent frameworks. Each has pros and cons. Bedrock is good if you're already on AWS. LangChain is good if you want flexibility.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Start small.&lt;/strong&gt; Don't try to automate everything at once. Pick one simple workflow and get that working first.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Build in safety from day one.&lt;/strong&gt; Set clear boundaries on what the agent can do. Log everything. Start with requiring approval for actions, then relax that as you gain confidence.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Test thoroughly.&lt;/strong&gt; Run your agent through every scenario you can think of. What happens when things go wrong? How does it handle edge cases?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Monitor and improve.&lt;/strong&gt; Once it's running, watch it closely at first. You'll find ways to improve it based on how it actually performs.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Cost:&lt;/strong&gt; $100-1000+/month depending on what you're building&lt;br&gt;
&lt;strong&gt;Time to start:&lt;/strong&gt; Days to weeks&lt;br&gt;
&lt;strong&gt;Complexity:&lt;/strong&gt; Medium - you'll need some technical skills or help&lt;/p&gt;




&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;Here's what you need to remember from all of this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI assistants are for thinking.&lt;/strong&gt; They help you understand, plan, create, and learn. They're your smart advisor who knows a lot but can't touch anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agentic AI is for doing.&lt;/strong&gt; They handle workflows, take actions, make decisions, and complete tasks. They're your capable employee who can handle projects independently.&lt;/p&gt;

&lt;p&gt;Both are valuable. Both have their place. The question isn't "which is better?" The question is "which solves my problem?"&lt;/p&gt;

&lt;p&gt;Need answers? Use an assistant.&lt;br&gt;
Need actions? Use an agent.&lt;br&gt;
Need both? Use both.&lt;/p&gt;

&lt;p&gt;We're in 2026, and we're just starting to figure out what's possible with AI that can actually do things. The assistants made us smarter. The agents are making us more productive.&lt;/p&gt;

&lt;p&gt;The real power comes from knowing when to use each one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick Reference Guide
&lt;/h2&gt;

&lt;p&gt;Still not sure which you need? Ask yourself these questions:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Does it just need to answer, or does it need to act?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Just answer → Assistant&lt;/li&gt;
&lt;li&gt;Actually act → Agent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Do I want to stay in control of every step?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Yes → Assistant&lt;/li&gt;
&lt;li&gt;No, I want it handled → Agent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Is this a one-time question or an ongoing workflow?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One-time → Assistant&lt;/li&gt;
&lt;li&gt;Ongoing → Agent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Am I trying to learn something or automate something?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Learn → Assistant&lt;/li&gt;
&lt;li&gt;Automate → Agent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Do I need this to work when I'm not around?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No → Assistant&lt;/li&gt;
&lt;li&gt;Yes → Agent&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Simple as that.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Means for Different People
&lt;/h2&gt;

&lt;h3&gt;
  
  
  If You're a Developer:
&lt;/h3&gt;

&lt;p&gt;Learn both. Understanding how to build assistants and agents is becoming an important skill. Start with assistants (easier), then explore agents (more complex). Focus on safety, monitoring, and good design patterns.&lt;/p&gt;

&lt;p&gt;Developers who understand agentic AI will have an advantage as this technology matures.&lt;/p&gt;

&lt;h3&gt;
  
  
  If You're Running a Business:
&lt;/h3&gt;

&lt;p&gt;Look at your workflows. Where are people doing repetitive, multi-step tasks? Those are agent candidates. Where do people need information and advice? Those are assistant use cases.&lt;/p&gt;

&lt;p&gt;Start with one agent for one workflow. Measure the impact. If it works, expand. Don't try to automate everything at once.&lt;/p&gt;

&lt;p&gt;And remember: the goal isn't to replace people. It's to free them up to do more valuable work.&lt;/p&gt;

&lt;h3&gt;
  
  
  If You're Just Curious:
&lt;/h3&gt;

&lt;p&gt;Try ChatGPT or Claude if you haven't already. That's an assistant. Play with it. See what it can and can't do.&lt;/p&gt;

&lt;p&gt;Then watch for agent capabilities showing up in tools you use. GitHub Copilot, AWS services, automation platforms - agents are being built into everything.&lt;/p&gt;

&lt;p&gt;Understanding this difference will help you make sense of where AI is actually going.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;The AI revolution isn't just about smarter answers. It's about AI that can actually do things.&lt;/p&gt;

&lt;p&gt;For years, we've had AI that could think but not act. Now we have AI that can do both. That's a fundamental shift, and most people haven't caught up to it yet.&lt;/p&gt;

&lt;p&gt;AI assistants made us more informed. They gave us access to knowledge and helped us think through problems. That's valuable, and it's not going away.&lt;/p&gt;

&lt;p&gt;Agentic AI is making us more productive. It's taking tasks off our plate and handling them autonomously. That's powerful, and it's just getting started.&lt;/p&gt;

&lt;p&gt;The key is understanding which tool solves which problem. Don't use an assistant when you need an agent. Don't use an agent when you just need advice.&lt;/p&gt;

&lt;p&gt;We're at the beginning of the agentic AI era. The assistants aren't going away - they're just getting company. And together, they're changing how we work.&lt;/p&gt;

&lt;p&gt;The question isn't whether you should use AI. The question is: which AI should you use, and for what?&lt;/p&gt;

&lt;p&gt;Now you know the answer.&lt;/p&gt;




&lt;h2&gt;
  
  
  📌 Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Thank you for reading! I hope this article gave you practical insights and a clearer perspective on the topic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Was this helpful?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❤️ Like if it added value&lt;/li&gt;
&lt;li&gt;🦄 Unicorn if you’re applying it today&lt;/li&gt;
&lt;li&gt;💾 Save for your next optimization session&lt;/li&gt;
&lt;li&gt;🔄 Share with your team&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Follow me for more on:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS architecture patterns&lt;/li&gt;
&lt;li&gt;FinOps automation&lt;/li&gt;
&lt;li&gt;Multi-account strategies&lt;/li&gt;
&lt;li&gt;AI-driven DevOps&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💡 What’s Next
&lt;/h2&gt;

&lt;p&gt;More deep dives coming soon on cloud operations, GenAI, Agentic-AI, DevOps, and data workflows follow for weekly insights.&lt;/p&gt;




&lt;h2&gt;
  
  
  🌐 Portfolio &amp;amp; Work
&lt;/h2&gt;

&lt;p&gt;You can explore my full body of work, certifications, architecture projects, and technical articles here:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://sarvarnadaf.com" rel="noopener noreferrer"&gt;Visit My Website&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Services I Offer
&lt;/h2&gt;

&lt;p&gt;If you're looking for hands-on guidance or collaboration, I provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloud Architecture Consulting (AWS / Azure)&lt;/li&gt;
&lt;li&gt;DevSecOps &amp;amp; Automation Design&lt;/li&gt;
&lt;li&gt;FinOps Optimization Reviews&lt;/li&gt;
&lt;li&gt;Technical Writing (Cloud, DevOps, GenAI)&lt;/li&gt;
&lt;li&gt;Product &amp;amp; Architecture Reviews&lt;/li&gt;
&lt;li&gt;Mentorship &amp;amp; 1:1 Technical Guidance&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🤝 Let’s Connect
&lt;/h2&gt;

&lt;p&gt;I’d love to hear your thoughts drop a comment or connect with me on &lt;a href="https://www.linkedin.com/in/sarvar04/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For collaborations, consulting, or technical discussions, feel free to reach out directly at &lt;a href="mailto:simplynadaf@gmail.com"&gt;simplynadaf@gmail.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Happy Learning&lt;/strong&gt; 🚀&lt;/p&gt;

</description>
      <category>ai</category>
      <category>aws</category>
      <category>discuss</category>
      <category>agents</category>
    </item>
  </channel>
</rss>
