<?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: Ihuoma Lilian</title>
    <description>The latest articles on DEV Community by Ihuoma Lilian (@ihuoma_lilian_8c9eff4fe75).</description>
    <link>https://dev.to/ihuoma_lilian_8c9eff4fe75</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3991502%2Fe25c7675-eff9-40df-9e8c-0ef49356907b.png</url>
      <title>DEV Community: Ihuoma Lilian</title>
      <link>https://dev.to/ihuoma_lilian_8c9eff4fe75</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ihuoma_lilian_8c9eff4fe75"/>
    <language>en</language>
    <item>
      <title>"My First Terraform Workflow on Azure:</title>
      <dc:creator>Ihuoma Lilian</dc:creator>
      <pubDate>Sat, 11 Jul 2026 16:31:29 +0000</pubDate>
      <link>https://dev.to/ihuoma_lilian_8c9eff4fe75/my-first-terraform-workflow-on-azure-3hdi</link>
      <guid>https://dev.to/ihuoma_lilian_8c9eff4fe75/my-first-terraform-workflow-on-azure-3hdi</guid>
      <description>&lt;p&gt;A hands-on walkthrough of provisioning and tearing down my first Azure resource using Terraform as part of a learning scenario for Hagital Cloud Solutions.&lt;br&gt;
What is Terraform?&lt;br&gt;
Terraform is an Infrastructure as Code (IaC) tool that automates the provisioning and management of cloud resources. Instead of clicking through a portal to create resources by hand, you describe the infrastructure you want in code, and Terraform figures out how to make the real world match that description.&lt;br&gt;
Because the infrastructure lives in code, it's:&lt;br&gt;
• Consistent:- the same configuration produces the same environment every time.&lt;br&gt;
• Repeatable:- spin up (or tear down) an entire environment on demand.&lt;br&gt;
• Version-controlled:-  infrastructure changes go through the same review process as application code, via Git.&lt;br&gt;
This works across cloud platforms AWS, Google Cloud, Microsoft Azure, and many more which is a big part of why it's become the standard tool for IaC. For this walkthrough, I used it against Microsoft Azure, and the goal was intentionally small: create one Resource Group, verify it, then clean it up.&lt;br&gt;
How Terraform Works&lt;br&gt;
Terraform follows a simple, repeatable workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Write:-Create configuration files (.tf files) that describe the desired infrastructure.&lt;/li&gt;
&lt;li&gt; Initialize:- Run terraform init to download the required provider plugins (in this case, the Azure provider).&lt;/li&gt;
&lt;li&gt; Format &amp;amp; Validate:- Run terraform fmt to keep the code style consistent, and terraform validate to catch configuration errors before touching any real infrastructure.&lt;/li&gt;
&lt;li&gt; Plan:-  Run terraform plan to preview exactly what Terraform will create, change, or destroy — nothing happens to real resources at this stage.&lt;/li&gt;
&lt;li&gt; Apply:- Run terraform apply to actually provision (or update) the infrastructure.
Once you're done with the environment, there's a sixth step that's just as important:&lt;/li&gt;
&lt;li&gt; terraform destroy, which tears everything back down. That last step is easy to forget, and it's exactly how unused cloud resources quietly rack up a bill.
The Scenario: Hagital Cloud Solutions
For this exercise, I used a simple scenario: Hagital Cloud Solutions needs a single Azure Resource Group named hagital-rg to serve as a container for future resources. The task was to go through the complete Terraform lifecycle write, init, fmt, validate, plan, apply, verify, and destroy and document each step.
The Configuration
I split the config into a couple of small files rather than one giant main.tf, mostly to keep provider setup separate from the actual resource definition.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff3oaoszmz71nxugs25py.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff3oaoszmz71nxugs25py.png" alt=" " width="566" height="314"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;main.tf — the actual resource:&lt;br&gt;
Step 1  Authenticate&lt;br&gt;
Before Terraform can talk to Azure, the Azure CLI needs to be logged in:&lt;br&gt;
az login&lt;br&gt;
az account show   # confirms the right subscription is active&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm5d53wpeqhmygc18a46g.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm5d53wpeqhmygc18a46g.png" alt=" " width="747" height="464"&gt;&lt;/a&gt;&lt;br&gt;
Step 2  Initialize&lt;br&gt;
terraform init&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F778v26lbkz9ua5as4knv.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F778v26lbkz9ua5as4knv.png" alt=" " width="432" height="253"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This downloads the hashicorp/azurerm provider plugin and sets up the local .terraform working directory. This is also the step where a typo in the provider source (I had harshicorp/azurerm at one point  an easy typo to make) will fail loudly, since Terraform treats provider names literally with no fuzzy matching.&lt;br&gt;
Step 3 Format and Validate&lt;br&gt;
terraform fmt&lt;br&gt;
terraform validate&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnw0dku87vk0v4p9f8ded.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnw0dku87vk0v4p9f8ded.png" alt=" " width="528" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;fmt rewrites the main.tf files into Terraform's canonical style consistent indentation and spacing so the code stays readable no matter who wrote it. validate then checks that the configuration is internally consistent (correct syntax, valid argument names, and so on) without reaching out to Azure at all.&lt;br&gt;
A clean run looks like:&lt;br&gt;
Success! The configuration is valid.&lt;br&gt;
Step 4 Plan&lt;br&gt;
terraform plan&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwsnuzqd8k9gcxx0w0do4.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwsnuzqd8k9gcxx0w0do4.png" alt=" " width="438" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is Terraform's "dry run" it compares the code against the current state of the real infrastructure and prints exactly what it intends to do, without doing it yet:&lt;br&gt;
Plan: 1 to add, 0 to change, 0 to destroy.&lt;br&gt;
Getting into the habit of reading the plan output carefully before applying is probably the single most useful habit from this whole exercise  it's the difference between infrastructure changes being predictable versus a surprise.&lt;br&gt;
Step 5 Apply&lt;/p&gt;

&lt;p&gt;terraform apply&lt;br&gt;
After confirming with yes, Terraform provisions the resource group:&lt;br&gt;
Apply complete! Resources: 1 added, 0 changed, 0 destroyed.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwk2w9r8f2l6sjw2l2h62.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwk2w9r8f2l6sjw2l2h62.png" alt=" " width="635" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 6 Verify in the Azure Portal&lt;br&gt;
Jumping into portal.azure.com → Resource groups, hagital-rg shows up in the region I specified that is central US confirmation that Terraform's state and the real Azure environment actually match.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe10rmbf40c946g92gai7.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fe10rmbf40c946g92gai7.png" alt=" " width="800" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Step 7 Destroy&lt;br&gt;
Once the resource had served its purpose (verifying the workflow), it made no sense to leave it running:&lt;br&gt;
terraform destroy&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnnvfxpmtir9d53ci589w.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnnvfxpmtir9d53ci589w.png" alt=" " width="479" height="256"&gt;&lt;/a&gt;&lt;br&gt;
Terraform shows the same kind of preview as plan, but this time for deletion, and asks for explicit confirmation before doing anything:&lt;br&gt;
Do you really want to destroy all resources?&lt;br&gt;
  Terraform will destroy all your managed infrastructure...&lt;br&gt;
  Enter a value: yes&lt;/p&gt;

&lt;p&gt;Destroy complete! Resources: 1 destroyed.&lt;/p&gt;

&lt;p&gt;What I Learned&lt;br&gt;
Working through this end-to-end, even for something as simple as deploying a single Azure resource group, taught me a few valuable lessons.&lt;br&gt;
First, I realized that the Terraform workflow itself is just as important as the infrastructure you're deploying. Commands like terraform init, fmt, validate, plan, and apply each serve a purpose, helping you catch different types of issues before any changes are made to your cloud environment.&lt;br&gt;
I also learned that not every error is caused by Terraform. For example, authentication errors such as "could not acquire access token" often point to issues with Azure CLI authentication or network connectivity rather than problems in the Terraform configuration.&lt;br&gt;
Another takeaway was how useful terraform plan is. Seeing a preview of the changes before they are applied gave me confidence that Terraform would do exactly what I expected, making deployments feel much less risky.&lt;br&gt;
Finally, I discovered that cleaning up resources is just as important as creating them. Using terraform destroy ensures that temporary or practice environments are removed when they're no longer needed, helping to avoid unnecessary cloud costs.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>devops</category>
      <category>ai</category>
      <category>cybersecurity</category>
    </item>
  </channel>
</rss>
