<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Terraform on IT Comparison</title><link>https://comparison.metacog.co.kr/tags/terraform/</link><description>Recent content in Terraform on IT Comparison</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Sun, 02 Aug 2026 11:25:27 +0900</lastBuildDate><atom:link href="https://comparison.metacog.co.kr/tags/terraform/index.xml" rel="self" type="application/rss+xml"/><item><title>Terraform vs Ansible: Provisioning vs Configuration Management</title><link>https://comparison.metacog.co.kr/posts/2026-08-02-terraform-vs-ansible-provisioning-vs-configuration-managemen/</link><pubDate>Sun, 02 Aug 2026 11:25:27 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-02-terraform-vs-ansible-provisioning-vs-configuration-managemen/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Terraform and Ansible are both infrastructure-as-code tools but solve different problems: Terraform declaratively provisions and tracks cloud infrastructure using a state file, while Ansible procedurally configures and manages software on existing hosts with no persistent state. Many teams use them together — Terraform to stand up infrastructure, Ansible to configure it.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg"&gt;&lt;line x1="320" y1="20" x2="320" y2="335" style="stroke:var(--border)" stroke-width="1.5" stroke-dasharray="4 4"/&gt;&lt;text x="170" y="28" text-anchor="middle" style="fill:var(--primary)" font-size="16" font-weight="bold"&gt;Terraform&lt;/text&gt;&lt;text x="170" y="44" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;declarative&lt;/text&gt;&lt;rect x="70" y="52" width="200" height="32" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="170" y="72" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;main.tf (desired state)&lt;/text&gt;&lt;line x1="170" y1="84" x2="170" y2="98" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;rect x="70" y="98" width="200" height="32" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="170" y="118" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;state file (source of truth)&lt;/text&gt;&lt;line x1="170" y1="130" x2="170" y2="152" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;rect x="110" y="152" width="120" height="30" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="170" y="171" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;dependency graph&lt;/text&gt;&lt;line x1="150" y1="182" x2="65" y2="244" style="stroke:var(--compare-a)" stroke-width="1.2"/&gt;&lt;line x1="170" y1="182" x2="155" y2="244" style="stroke:var(--compare-a)" stroke-width="1.2"/&gt;&lt;line x1="190" y1="182" x2="245" y2="244" style="stroke:var(--compare-a)" stroke-width="1.2"/&gt;&lt;rect x="25" y="244" width="80" height="32" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="65" y="264" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;VM&lt;/text&gt;&lt;rect x="115" y="244" width="80" height="32" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="155" y="264" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Network&lt;/text&gt;&lt;rect x="205" y="244" width="80" height="32" rx="4" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="245" y="264" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;DB&lt;/text&gt;&lt;text x="170" y="300" text-anchor="middle" style="fill:var(--secondary)" font-size="9.5"&gt;converges infra to match state&lt;/text&gt;&lt;text x="480" y="28" text-anchor="middle" style="fill:var(--primary)" font-size="16" font-weight="bold"&gt;Ansible&lt;/text&gt;&lt;text x="480" y="44" text-anchor="middle" style="fill:var(--secondary)" font-size="11"&gt;procedural&lt;/text&gt;&lt;rect x="380" y="52" width="200" height="32" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="72" text-anchor="middle" style="fill:var(--content)" font-size="11"&gt;playbook.yml&lt;/text&gt;&lt;line x1="480" y1="84" x2="480" y2="96" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;rect x="400" y="96" width="160" height="26" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="113" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;task 1: install pkg&lt;/text&gt;&lt;line x1="480" y1="122" x2="480" y2="132" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;rect x="400" y="132" width="160" height="26" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="149" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;task 2: configure&lt;/text&gt;&lt;line x1="480" y1="158" x2="480" y2="168" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;rect x="400" y="168" width="160" height="26" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="185" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;task 3: start service&lt;/text&gt;&lt;line x1="460" y1="194" x2="385" y2="244" style="stroke:var(--compare-b)" stroke-width="1.2" stroke-dasharray="3 3"/&gt;&lt;line x1="480" y1="194" x2="475" y2="244" style="stroke:var(--compare-b)" stroke-width="1.2" stroke-dasharray="3 3"/&gt;&lt;line x1="500" y1="194" x2="565" y2="244" style="stroke:var(--compare-b)" stroke-width="1.2" stroke-dasharray="3 3"/&gt;&lt;rect x="345" y="244" width="80" height="32" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="385" y="264" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Server A&lt;/text&gt;&lt;rect x="435" y="244" width="80" height="32" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="475" y="264" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Server B&lt;/text&gt;&lt;rect x="525" y="244" width="80" height="32" rx="4" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="565" y="264" text-anchor="middle" style="fill:var(--content)" font-size="10"&gt;Server C&lt;/text&gt;&lt;text x="480" y="300" text-anchor="middle" style="fill:var(--secondary)" font-size="9.5"&gt;sequential push over SSH, no state file&lt;/text&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Terraform&lt;/th&gt;
&lt;th&gt;Ansible&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Primary purpose&lt;/td&gt;
&lt;td&gt;Provision and tear down cloud/infra resources (VMs, networks, DBs)&lt;/td&gt;
&lt;td&gt;Configure software and manage state on existing hosts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Configuration language&lt;/td&gt;
&lt;td&gt;HCL (HashiCorp Configuration Language), declarative&lt;/td&gt;
&lt;td&gt;YAML playbooks, procedural task lists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Execution model&lt;/td&gt;
&lt;td&gt;Builds a dependency graph and applies changes in parallel where possible&lt;/td&gt;
&lt;td&gt;Executes tasks sequentially, in order, per host&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State management&lt;/td&gt;
&lt;td&gt;Maintains a state file mapping config to real resources&lt;/td&gt;
&lt;td&gt;Stateless — queries live system facts on each run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Idempotency approach&lt;/td&gt;
&lt;td&gt;Diffs desired config against state file before acting&lt;/td&gt;
&lt;td&gt;Each module checks current condition before making a change&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connectivity/agent requirement&lt;/td&gt;
&lt;td&gt;Agentless — calls cloud/provider APIs directly&lt;/td&gt;
&lt;td&gt;Agentless — connects over SSH or WinRM to target hosts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure &amp;amp; recovery handling&lt;/td&gt;
&lt;td&gt;Partial applies are resolved by re-running against the state file&lt;/td&gt;
&lt;td&gt;Reruns the playbook from the start; tasks are re-checked, not resumed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Terraform tracks infrastructure in a persistent &lt;strong class="kw"&gt;state file&lt;/strong&gt;; Ansible has no state store and reads live system facts each run.&lt;/li&gt;
&lt;li&gt;Terraform resolves a &lt;strong class="kw"&gt;dependency graph&lt;/strong&gt; to apply changes in parallel; Ansible runs tasks &lt;strong class="kw"&gt;sequentially&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Terraform talks to infrastructure through provider &lt;strong class="kw"&gt;APIs&lt;/strong&gt;; Ansible connects to hosts via &lt;strong class="kw"&gt;SSH/WinRM&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Terraform is built for &lt;strong class="kw"&gt;provisioning&lt;/strong&gt; infra; Ansible is built for &lt;strong class="kw"&gt;configuration&lt;/strong&gt; of what already exists.&lt;/li&gt;
&lt;li&gt;They&amp;rsquo;re commonly paired: Terraform creates the servers, then Ansible &lt;strong class="kw"&gt;configures&lt;/strong&gt; them.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Terraform&lt;/strong&gt;&lt;/p&gt;</description></item><item><title>Declarative vs Imperative IaC: Describing the End State vs Scripting the Steps</title><link>https://comparison.metacog.co.kr/posts/2026-08-02-declarative-vs-imperative-iac-describing-the-end-state-vs-sc/</link><pubDate>Sun, 02 Aug 2026 08:55:05 +0900</pubDate><guid>https://comparison.metacog.co.kr/posts/2026-08-02-declarative-vs-imperative-iac-describing-the-end-state-vs-sc/</guid><description>&lt;h2 id="overview"&gt;Overview&lt;/h2&gt;
&lt;p&gt;Declarative IaC (e.g. Terraform, CloudFormation) has you specify the desired end state of infrastructure and lets an engine figure out how to get there. Imperative IaC (e.g. shell scripts, Chef recipes, raw CLI calls) has you write the exact sequence of commands to execute. The distinction matters because it determines who — you or the tool — is responsible for ordering, idempotency, and reconciling drift.&lt;/p&gt;
&lt;h2 id="comparison-diagram"&gt;Comparison Diagram&lt;/h2&gt;
&lt;div class="compare-diagram"&gt;
&lt;svg viewBox="0 0 640 360" xmlns="http://www.w3.org/2000/svg"&gt;&lt;text x="160" y="28" text-anchor="middle" font-size="16" style="fill:var(--primary)"&gt;Declarative&lt;/text&gt;&lt;rect x="40" y="50" width="240" height="55" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="72" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Desired State&lt;/text&gt;&lt;text x="160" y="90" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;"3 servers, 1 LB"&lt;/text&gt;&lt;line x1="160" y1="105" x2="160" y2="130" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;polygon points="160,140 155,130 165,130" style="fill:var(--compare-a)"/&gt;&lt;rect x="40" y="145" width="240" height="55" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="167" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Engine Computes Diff&lt;/text&gt;&lt;text x="160" y="185" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;plan + dependency graph&lt;/text&gt;&lt;line x1="160" y1="200" x2="160" y2="225" style="stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;polygon points="160,235 155,225 165,225" style="fill:var(--compare-a)"/&gt;&lt;rect x="40" y="240" width="240" height="55" rx="6" style="fill:var(--compare-a-soft);stroke:var(--compare-a)" stroke-width="1.5"/&gt;&lt;text x="160" y="262" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Infrastructure&lt;/text&gt;&lt;text x="160" y="280" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;converges to match state&lt;/text&gt;&lt;text x="160" y="320" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;Engine decides how &amp;amp; in what order&lt;/text&gt;&lt;line x1="320" y1="20" x2="320" y2="340" style="stroke:var(--border)" stroke-width="1" stroke-dasharray="4,4"/&gt;&lt;text x="480" y="28" text-anchor="middle" font-size="16" style="fill:var(--primary)"&gt;Imperative&lt;/text&gt;&lt;rect x="360" y="45" width="240" height="40" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="70" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Step 1: Create VPC&lt;/text&gt;&lt;line x1="480" y1="85" x2="480" y2="100" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;polygon points="480,110 475,100 485,100" style="fill:var(--compare-b)"/&gt;&lt;rect x="360" y="115" width="240" height="40" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="140" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Step 2: Launch Servers&lt;/text&gt;&lt;line x1="480" y1="155" x2="480" y2="170" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;polygon points="480,180 475,170 485,170" style="fill:var(--compare-b)"/&gt;&lt;rect x="360" y="185" width="240" height="40" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="210" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Step 3: Attach LB&lt;/text&gt;&lt;line x1="480" y1="225" x2="480" y2="240" style="stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;polygon points="480,250 475,240 485,240" style="fill:var(--compare-b)"/&gt;&lt;rect x="360" y="255" width="240" height="40" rx="6" style="fill:var(--compare-b-soft);stroke:var(--compare-b)" stroke-width="1.5"/&gt;&lt;text x="480" y="280" text-anchor="middle" font-size="12" style="fill:var(--content)"&gt;Infrastructure&lt;/text&gt;&lt;text x="480" y="320" text-anchor="middle" font-size="11" style="fill:var(--secondary)"&gt;Author decides exact steps &amp;amp; order&lt;/text&gt;&lt;/svg&gt;
&lt;/div&gt;
&lt;h2 id="comparison-table"&gt;Comparison Table&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Declarative&lt;/th&gt;
&lt;th&gt;Imperative&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Authoring model&lt;/td&gt;
&lt;td&gt;Write a &lt;strong class="kw"&gt;desired-state spec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Write an &lt;strong class="kw"&gt;ordered command list&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Execution engine&lt;/td&gt;
&lt;td&gt;Resolves a &lt;strong class="kw"&gt;dependency graph&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Runs a &lt;strong class="kw"&gt;sequential interpreter&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State tracking&lt;/td&gt;
&lt;td&gt;Maintains a &lt;strong class="kw"&gt;state file&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong class="kw"&gt;Stateless&lt;/strong&gt; execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Applying changes&lt;/td&gt;
&lt;td&gt;Single &lt;strong class="kw"&gt;apply&lt;/strong&gt; command&lt;/td&gt;
&lt;td&gt;Run the &lt;strong class="kw"&gt;script/playbook&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ordering &amp;amp; dependencies&lt;/td&gt;
&lt;td&gt;&lt;strong class="kw"&gt;Auto-resolved&lt;/strong&gt; by engine&lt;/td&gt;
&lt;td&gt;&lt;strong class="kw"&gt;Manually sequenced&lt;/strong&gt; by author&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Idempotency&lt;/td&gt;
&lt;td&gt;&lt;strong class="kw"&gt;Guaranteed&lt;/strong&gt; by design&lt;/td&gt;
&lt;td&gt;&lt;strong class="kw"&gt;Developer-enforced&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Drift detection&lt;/td&gt;
&lt;td&gt;Built-in &lt;strong class="kw"&gt;plan diff&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong class="kw"&gt;Not built-in&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure handling&lt;/td&gt;
&lt;td&gt;Partial apply, &lt;strong class="kw"&gt;replan&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong class="kw"&gt;Manual rollback&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="key-differences"&gt;Key Differences&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Declarative code answers &amp;lsquo;what&amp;rsquo;, imperative code answers &lt;strong class="kw"&gt;&amp;lsquo;how&amp;rsquo;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Declarative tools rely on a &lt;strong class="kw"&gt;state file&lt;/strong&gt; to know current vs. desired infrastructure; imperative scripts have no memory of prior runs.&lt;/li&gt;
&lt;li&gt;Idempotent re-runs are &lt;strong class="kw"&gt;automatic&lt;/strong&gt; in declarative tools but must be hand-coded (checks, conditionals) in imperative scripts.&lt;/li&gt;
&lt;li&gt;Declarative engines build a &lt;strong class="kw"&gt;dependency graph&lt;/strong&gt; to order operations; imperative code hardcodes that order line by line.&lt;/li&gt;
&lt;li&gt;Drift correction in declarative IaC is a matter of re-running &lt;strong class="kw"&gt;plan/apply&lt;/strong&gt;; imperative approaches require re-running or rewriting the exact script.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="when-to-use-each"&gt;When to Use Each&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Declarative&lt;/strong&gt;&lt;/p&gt;</description></item></channel></rss>