<?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: E-Learning Sherdil</title>
    <description>The latest articles on DEV Community by E-Learning Sherdil (@elearning_sherdil_783bbb).</description>
    <link>https://dev.to/elearning_sherdil_783bbb</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%2F3964965%2F93f4560a-e89f-48d7-afa6-dfabad7708c7.png</url>
      <title>DEV Community: E-Learning Sherdil</title>
      <link>https://dev.to/elearning_sherdil_783bbb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/elearning_sherdil_783bbb"/>
    <language>en</language>
    <item>
      <title>Terraform Tutorial for Beginners: Infrastructure as Code</title>
      <dc:creator>E-Learning Sherdil</dc:creator>
      <pubDate>Wed, 03 Jun 2026 05:45:54 +0000</pubDate>
      <link>https://dev.to/elearning_sherdil_783bbb/terraform-tutorial-for-beginners-infrastructure-as-code-170o</link>
      <guid>https://dev.to/elearning_sherdil_783bbb/terraform-tutorial-for-beginners-infrastructure-as-code-170o</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;📌 &lt;strong&gt;This article was originally published on &lt;a href="https://elearning.sherdil.org/pages/terraform-tutorial-beginners-iac" rel="noopener noreferrer"&gt;Sherdil E-Learning&lt;/a&gt;.&lt;/strong&gt; I'm republishing it here so the dev.to community can benefit too.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you have ever clicked through the AWS console, Azure portal, or GCP dashboard to create cloud resources, you know the pain: it's slow, hard to repeat reliably across staging and production, and easy to get wrong. &lt;strong&gt;Infrastructure as Code (IaC)&lt;/strong&gt; solves this problem, and &lt;strong&gt;Terraform&lt;/strong&gt; is the most widely used IaC tool in industry.&lt;/p&gt;

&lt;p&gt;This guide takes you from "what is IaC" to running your first Terraform deployment on AWS, then walks through a working VPC + EC2 example you can adapt for production. It also covers the BUSL / OpenTofu split from 2023 that every Terraform learner in 2026 should understand.&lt;/p&gt;




&lt;h2&gt;
  
  
  What is Infrastructure as Code?
&lt;/h2&gt;

&lt;p&gt;Infrastructure as code is the practice of managing cloud resources — servers, databases, networks, storage, security groups — through &lt;strong&gt;configuration files&lt;/strong&gt; rather than manual clicks in a web console. You describe the infrastructure you want in text files, store those files in Git, and let an IaC tool create and update the real resources to match.&lt;/p&gt;

&lt;p&gt;The practical benefits compound:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repeatability&lt;/strong&gt; — the same files produce the same infrastructure every time, which removes the "I forgot to open port 443" class of mistake.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit log built in&lt;/strong&gt; — Git history becomes the audit log for every change, and pull requests become the review process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed&lt;/strong&gt; — a complete environment (VPC, subnets, instances, load balancer, database) can spin up in minutes instead of hours of console work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-documenting&lt;/strong&gt; — the configuration files double as documentation: anyone joining the team can read the Terraform code and understand exactly what is deployed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reusable patterns&lt;/strong&gt; — once you have written a module for, say, a standard VPC, you can reuse it across every project with different inputs.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What is Terraform — and a note on OpenTofu
&lt;/h2&gt;

&lt;p&gt;Terraform is an open-source-style infrastructure-as-code tool originally created by HashiCorp in 2014. It uses a declarative configuration language called &lt;strong&gt;HCL (HashiCorp Configuration Language)&lt;/strong&gt; and supports hundreds of cloud and SaaS providers through a shared provider model.&lt;/p&gt;

&lt;p&gt;One thing every Terraform learner in 2026 should know: in &lt;strong&gt;August 2023&lt;/strong&gt;, HashiCorp changed Terraform's license from MPL 2.0 to the &lt;strong&gt;Business Source License (BUSL)&lt;/strong&gt;, which restricts commercial competitive use. In response, the &lt;strong&gt;OpenTofu&lt;/strong&gt; project was forked from Terraform 1.5.5, kept the MPL 2.0 license, and is now maintained by the Linux Foundation.&lt;/p&gt;

&lt;p&gt;OpenTofu is a drop-in replacement: same HCL syntax, same workflow, same provider ecosystem.&lt;/p&gt;

&lt;p&gt;For learners, the practical difference is small. &lt;strong&gt;Terraform has more name recognition with employers&lt;/strong&gt;, so start with Terraform for employability. If your future employer prefers OpenTofu, the switch is trivial. The rest of this tutorial works for both.&lt;/p&gt;

&lt;p&gt;Official sites: &lt;a href="https://terraform.io" rel="noopener noreferrer"&gt;terraform.io&lt;/a&gt; for Terraform; &lt;a href="https://opentofu.org" rel="noopener noreferrer"&gt;opentofu.org&lt;/a&gt; for OpenTofu.&lt;/p&gt;




&lt;h2&gt;
  
  
  Terraform vs other IaC tools
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;th&gt;Language&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Terraform&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multi-cloud (AWS, Azure, GCP, Alibaba, +hundreds)&lt;/td&gt;
&lt;td&gt;HCL (declarative)&lt;/td&gt;
&lt;td&gt;Teams working across more than one cloud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS CloudFormation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AWS only&lt;/td&gt;
&lt;td&gt;YAML / JSON&lt;/td&gt;
&lt;td&gt;AWS-only shops wanting tight native integration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pulumi&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multi-cloud&lt;/td&gt;
&lt;td&gt;Python, TypeScript, Go, C#&lt;/td&gt;
&lt;td&gt;Teams that want a real programming language for infra&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ansible&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Configuration management&lt;/td&gt;
&lt;td&gt;YAML&lt;/td&gt;
&lt;td&gt;Configuring software on existing servers (different category)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Terraform's biggest advantage is &lt;strong&gt;multi-cloud support&lt;/strong&gt; — same syntax, every provider. CloudFormation is AWS-only and tightly integrated. Pulumi appeals to teams that want a real programming language. Ansible occupies a different category — it's better for configuring software on existing servers than provisioning the servers themselves.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Terraform works: write, plan, apply
&lt;/h2&gt;

&lt;p&gt;Terraform's daily workflow is three commands. Once you know what each one does, the rest of the tool makes sense.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Write
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;.tf&lt;/code&gt; files (Terraform configuration files) that describe the infrastructure you want — resources like virtual machines, databases, networks, IAM roles. Each file is just plain text in HCL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: 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;Terraform reads your configuration files, compares them to the current state of your infrastructure, and shows you exactly what it will &lt;strong&gt;create, modify, or destroy — before changing anything&lt;/strong&gt;. This is your safety net.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Apply
&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;Terraform executes the changes shown in the plan, calling the cloud provider's API to create, update, or delete resources to match your configuration.&lt;/p&gt;

&lt;p&gt;There's also &lt;code&gt;terraform destroy&lt;/code&gt;, which removes everything Terraform created — useful for tearing down test environments to avoid charges.&lt;/p&gt;




&lt;h2&gt;
  
  
  Core Terraform concepts
&lt;/h2&gt;

&lt;p&gt;Five concepts come up constantly. Get comfortable with these and the rest of Terraform becomes a matter of looking up provider-specific resource names.&lt;/p&gt;

&lt;h3&gt;
  
  
  Providers
&lt;/h3&gt;

&lt;p&gt;Plugins that let Terraform interact with specific cloud platforms or services. The AWS provider talks to the AWS API, the Azure provider talks to the Azure API, and so on. You declare which provider you need and Terraform downloads the right plugin during &lt;code&gt;terraform init&lt;/code&gt;. Browse the full catalogue at &lt;a href="https://registry.terraform.io" rel="noopener noreferrer"&gt;registry.terraform.io&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;p&gt;The building blocks of your infrastructure. Each resource block defines one component — an EC2 instance, an S3 bucket, an IAM role, a VPC. The first label on a resource block is the &lt;strong&gt;type&lt;/strong&gt; (e.g. &lt;code&gt;aws_s3_bucket&lt;/code&gt;), the second is a &lt;strong&gt;name&lt;/strong&gt; you choose for referring to this resource elsewhere in your code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Variables
&lt;/h3&gt;

&lt;p&gt;Variables make your code reusable. Instead of hard-coding values like the AWS region or instance size, you declare variables and pass values at apply time. The same Terraform code can then deploy a small dev environment or a large production environment by changing only the variable values.&lt;/p&gt;

&lt;h3&gt;
  
  
  State
&lt;/h3&gt;

&lt;p&gt;Terraform maintains a state file (&lt;code&gt;terraform.tfstate&lt;/code&gt;) that records what infrastructure Terraform currently manages. The state file maps your configuration to real-world resources, so Terraform knows what to update, what to leave alone, and what to destroy.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;State management is the single most important production concern.&lt;/strong&gt; Never commit &lt;code&gt;terraform.tfstate&lt;/code&gt; to Git, and use a remote backend (S3, Azure Blob, Terraform Cloud) for any team work.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Modules
&lt;/h3&gt;

&lt;p&gt;Reusable packages of Terraform configuration. Think of a module as a function: you define one standard VPC setup, then call that module across staging, production, and every new project with different inputs. The Terraform Registry hosts thousands of open-source modules.&lt;/p&gt;




&lt;h2&gt;
  
  
  Your first Terraform project: a hands-on walkthrough
&lt;/h2&gt;

&lt;p&gt;Before you start you need three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An &lt;strong&gt;AWS account&lt;/strong&gt; with Free Tier access (sign up at &lt;a href="https://aws.amazon.com" rel="noopener noreferrer"&gt;aws.amazon.com&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AWS CLI&lt;/strong&gt; installed and configured with your access keys (run &lt;code&gt;aws configure&lt;/code&gt; after install).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terraform&lt;/strong&gt; installed — download from &lt;a href="https://terraform.io" rel="noopener noreferrer"&gt;terraform.io&lt;/a&gt; or install via your package manager.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 1: Create a 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;my-first-terraform &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;my-first-terraform
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Write a configuration file
&lt;/h3&gt;

&lt;p&gt;Create a file called &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="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="s2"&gt;"us-east-1"&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_s3_bucket"&lt;/span&gt; &lt;span class="s2"&gt;"my_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;"my-first-terraform-bucket-replace-this-name"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells Terraform to use the AWS provider in &lt;code&gt;us-east-1&lt;/code&gt; and create an S3 bucket. &lt;strong&gt;Replace the bucket name with something globally unique&lt;/strong&gt; — S3 bucket names must be unique across all of AWS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Initialise 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;This downloads the AWS provider plugin. Run it once per project, plus again when you add new providers or change provider versions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Preview the changes
&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;Terraform shows what it will do. In this case: &lt;code&gt;1 to add, 0 to change, 0 to destroy&lt;/code&gt; — creating one new S3 bucket. &lt;strong&gt;Always read the plan output before applying.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Apply the changes
&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. Terraform creates the S3 bucket. Verify it in the AWS S3 console.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: Clean up
&lt;/h3&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;Removes the S3 bucket so you don't incur any charges. Type &lt;code&gt;yes&lt;/code&gt; to confirm. This matters: Terraform makes it easy to create real resources accidentally, so &lt;strong&gt;always destroy what you don't need&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  A working VPC and EC2 example
&lt;/h2&gt;

&lt;p&gt;A more realistic configuration that creates a VPC, an Internet Gateway, a public subnet with routing, a security group, and an EC2 instance that is actually reachable from the internet on port 80. The AMI is fetched dynamically so the example doesn't rot when AWS publishes a new Amazon Linux image.&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;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="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Look up the latest Amazon Linux 2023 AMI dynamically&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="p"&gt;}&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="s2"&gt;"10.0.0.0/16"&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;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;"main-vpc"&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_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;"main-igw"&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_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="s2"&gt;"10.0.1.0/24"&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;"public-subnet"&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="p"&gt;}&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="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group"&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;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;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;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="p"&gt;}&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;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="s2"&gt;"t2.micro"&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;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;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;"web-server"&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;Notice how resources reference each other:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Internet Gateway attaches to the VPC (&lt;code&gt;aws_vpc.main.id&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The route table sits inside the VPC and forwards &lt;code&gt;0.0.0.0/0&lt;/code&gt; traffic to the gateway.&lt;/li&gt;
&lt;li&gt;The route table is then associated with the public subnet so instances in that subnet can reach the internet.&lt;/li&gt;
&lt;li&gt;The EC2 instance lives in the public subnet and uses the security group that allows port 80 inbound from anywhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Terraform works out the correct creation order automatically based on these references.&lt;/p&gt;




&lt;h2&gt;
  
  
  Best practices for beginners
&lt;/h2&gt;

&lt;p&gt;Five habits to build from your first project, in order of how much pain they save you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Always run &lt;code&gt;terraform plan&lt;/code&gt; before &lt;code&gt;terraform apply&lt;/code&gt;.&lt;/strong&gt; Read the plan output carefully, particularly look for any &lt;code&gt;destroy&lt;/code&gt; actions you didn't expect. One wrong configuration change can drop a production database. Treat plan output as a contract.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Use variables instead of hard-coded values.&lt;/strong&gt; Region, instance type, environment name, CIDR blocks, tags — none of these should be hard-coded. Create a separate &lt;code&gt;.tfvars&lt;/code&gt; file for each environment (&lt;code&gt;dev.tfvars&lt;/code&gt;, &lt;code&gt;staging.tfvars&lt;/code&gt;, &lt;code&gt;prod.tfvars&lt;/code&gt;) and pass the right one at apply time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Use a remote backend for state.&lt;/strong&gt; Once more than one person works on the codebase, store &lt;code&gt;terraform.tfstate&lt;/code&gt; in S3 (or Azure Blob, or Terraform Cloud) with state locking via DynamoDB. &lt;strong&gt;Never commit the state file to Git&lt;/strong&gt; — it contains secrets in plain text.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Extract reusable patterns into modules.&lt;/strong&gt; The first time you write a VPC, write it as plain resources. The second time, extract it into a module. By the third project you will save hours.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Tag every resource.&lt;/strong&gt; At minimum tag with environment (&lt;code&gt;dev&lt;/code&gt; / &lt;code&gt;staging&lt;/code&gt; / &lt;code&gt;prod&lt;/code&gt;), owner, and cost-centre. Untagged resources become impossible to attribute and trim when the AWS bill grows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is Terraform free?
&lt;/h3&gt;

&lt;p&gt;The Terraform CLI is free to download and use. Since August 2023 it is licensed under the &lt;strong&gt;Business Source License (BUSL)&lt;/strong&gt; rather than fully open-source, which restricts commercial competitive use but not normal day-to-day work. Terraform Cloud has a free tier for small teams (up to 500 resources) and paid tiers for larger organisations. If you specifically want a fully open-source IaC tool, &lt;strong&gt;OpenTofu&lt;/strong&gt; is the MPL-licensed fork.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need to know programming to use Terraform?
&lt;/h3&gt;

&lt;p&gt;No general-purpose programming is required. HCL is a configuration language, closer to JSON or YAML than to Python. If you can read and edit a JSON file, you can write Terraform. The mental model for variables, references, and modules will take a week or two but doesn't require previous coding experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I learn Terraform or CloudFormation first?
&lt;/h3&gt;

&lt;p&gt;If you work exclusively with AWS, either tool is fine. If you work with multiple cloud providers, or want the broadest job market, learn Terraform. Most DevOps job listings that mention IaC list Terraform first. Some AWS-only shops still use CloudFormation for tighter integration with AWS services.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is OpenTofu and should I use it instead of Terraform?
&lt;/h3&gt;

&lt;p&gt;OpenTofu is a Linux Foundation-maintained fork of Terraform 1.5.5, created in September 2023 after HashiCorp moved Terraform to the BUSL license. It keeps the MPL 2.0 open-source license and is largely a drop-in replacement — same HCL syntax, same workflow, same provider ecosystem. For learning, start with Terraform because more employers list it by name. Switching to OpenTofu later is straightforward if your company prefers it.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I store Terraform state safely?
&lt;/h3&gt;

&lt;p&gt;Never commit &lt;code&gt;terraform.tfstate&lt;/code&gt; to Git — it can contain secrets in plain text. For team work, use a remote backend: &lt;strong&gt;S3 with DynamoDB-based state locking&lt;/strong&gt; is the most common AWS pattern; &lt;strong&gt;Terraform Cloud&lt;/strong&gt; is the easiest if you want a managed solution; &lt;strong&gt;Azure Blob Storage&lt;/strong&gt; and &lt;strong&gt;Google Cloud Storage&lt;/strong&gt; work for Azure and GCP-centric teams. Configure the backend block in your Terraform configuration and run &lt;code&gt;terraform init&lt;/code&gt; to migrate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I use Terraform with Azure and GCP, not just AWS?
&lt;/h3&gt;

&lt;p&gt;Yes — Terraform's biggest strength is &lt;strong&gt;multi-cloud support&lt;/strong&gt;. The Azure provider (&lt;code&gt;azurerm&lt;/code&gt;), Google Cloud provider (&lt;code&gt;google&lt;/code&gt;), Alibaba Cloud provider (&lt;code&gt;alicloud&lt;/code&gt;), and hundreds of others all use the same workflow. The configuration syntax is the same; only the resource names change.&lt;/p&gt;

&lt;h3&gt;
  
  
  How long does it take to learn Terraform?
&lt;/h3&gt;

&lt;p&gt;You can deploy your first Terraform configuration in &lt;strong&gt;one to two weeks&lt;/strong&gt; of part-time study. Becoming proficient with modules, remote state, workspaces, and production patterns takes &lt;strong&gt;two to three months&lt;/strong&gt;. The &lt;strong&gt;Terraform Associate certification&lt;/strong&gt; typically takes four to six weeks of preparation on top of basic familiarity.&lt;/p&gt;




&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;The order I recommend at Sherdil:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get comfortable with the six-step S3 walkthrough above.&lt;/li&gt;
&lt;li&gt;Build the VPC + EC2 example end-to-end.&lt;/li&gt;
&lt;li&gt;Learn variables and modules.&lt;/li&gt;
&lt;li&gt;Set up a remote backend.&lt;/li&gt;
&lt;li&gt;Book the &lt;strong&gt;HashiCorp Certified: Terraform Associate (003)&lt;/strong&gt; exam — multiple-choice, ~$70 USD, the entry-level cert employers recognise.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a structured deep-dive, the &lt;a href="https://elearning.sherdil.org/pages/terraform-tutorial-beginners-iac" rel="noopener noreferrer"&gt;Mastering Terraform Course at Sherdil E-Learning&lt;/a&gt; covers everything above in depth plus the production-pattern material needed for the Terraform Associate exam. For learners who want IaC in the context of a full DevOps stack, the &lt;a href="https://elearning.sherdil.org" rel="noopener noreferrer"&gt;DevOps Engineer Course&lt;/a&gt; sequences Terraform alongside Docker, Kubernetes, AWS, and CI/CD.&lt;/p&gt;




&lt;h2&gt;
  
  
  About the author
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Muhammad Usman Khan&lt;/strong&gt; is a Lead Cloud Instructor at &lt;a href="https://elearning.sherdil.org" rel="noopener noreferrer"&gt;Sherdil E-Learning&lt;/a&gt;, holding the Alibaba Cloud ACP certification along with AWS and Azure credentials. He is an expert trainer in AWS and Google Cloud, having delivered &lt;strong&gt;1,500+ hours of training across 12+ countries&lt;/strong&gt; and successfully completed 50+ multi-cloud projects.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;💬 &lt;strong&gt;Found this useful?&lt;/strong&gt; Drop a ❤️ or a 🦄, and let me know in the comments what you'd like the next deep-dive to cover — remote backends, modules, or a Terraform Cloud walkthrough?&lt;/p&gt;

&lt;p&gt;📖 &lt;strong&gt;Full original article (with diagrams):&lt;/strong&gt; &lt;a href="https://elearning.sherdil.org/pages/terraform-tutorial-beginners-iac" rel="noopener noreferrer"&gt;elearning.sherdil.org/pages/terraform-tutorial-beginners-iac&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>terraform</category>
      <category>devops</category>
      <category>aws</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
