Microsoft certification badges banner
Headshot of Michael Korting

Blog

Microsoft 365 • Security • Compliance

Learning Terraform from the Language Up

Why my current Udemy learning path feels different from my original YouTube introduction to Terraform, and why understanding HCL may be more important than simply deploying another Azure resource.

My first serious introduction to Terraform was highly practical. I followed a YouTube course that moved almost immediately into Azure, authentication, providers, resource definitions, and deploying infrastructure. That experience gave me a useful first look at what Terraform could accomplish. It connected Infrastructure as Code to something familiar: creating and managing Azure resources.

That approach helped me get started, but my current Udemy learning path is taking me in a different direction.

Instead of beginning with the cloud resources I want to create, the course begins with the language, principles, and organizational practices behind the configuration. It is less about reaching Azure as quickly as possible and more about understanding why Terraform configurations are written and structured the way they are.

After completing approximately 50 modules, I am discovering that the difference is significant. The earlier training showed me Terraform in action. This learning path is helping me understand Terraform as a language and an engineering discipline.

The Value of the First Approach

The direct-to-Azure approach was not wrong. In fact, it provided exactly the sort of early result that can make a new technology feel approachable.

I could see the relationship between a Terraform configuration and the resulting cloud resources. Concepts such as providers, resource blocks, authentication, plans, state, and the standard Terraform workflow became more concrete because I could connect them to Azure services I already understood.

That first stage also reinforced the larger value of Infrastructure as Code. Rather than relying only on portal activity and procedural documentation, I could define what should exist in a set of configuration files. Terraform could evaluate that declaration and determine what changes were required.

My earlier work with Bicep and PowerShell had already introduced me to repeatable infrastructure and cloud automation. Terraform extended that thinking through a provider-based model and a common declarative workflow that was not limited to Azure.

The immediacy of the YouTube course was one of its strengths. Deploying a resource group or storage account makes IaC feel real. It delivers a visible result and provides a reason to continue learning.

However, visible progress and durable understanding are not always the same thing.

It is possible to follow an instructor, type the same configuration, run the same commands, and create the same resources without fully understanding the choices embedded in the example. The deployment may succeed, but the learner may not yet be prepared to restructure the configuration, troubleshoot an unfamiliar error, or reuse the design across multiple environments.

That is the gap my current learning path is beginning to address.

Starting with Infrastructure as Code Principles

The Udemy course begins by asking a more fundamental question: What is Infrastructure as Code?

From there, it introduces several concepts that initially sound more like software engineering than cloud administration:

  • Idempotence
  • Immutability
  • Encapsulation
  • Cohesion
  • Declarative versus imperative approaches
  • D.R.Y., or “Don’t Repeat Yourself”

Idempotence

Idempotence means that repeatedly applying the same intended configuration should not continually create duplicate resources or unnecessary changes. Once the managed environment matches the declared configuration, another evaluation should recognize that alignment.

This changes the administrative mindset. The objective is not to write a script that blindly repeats a series of steps. The objective is to describe a desired condition that Terraform can evaluate.

Immutability

Immutability introduces the idea that replacing an infrastructure component can sometimes be more predictable than repeatedly modifying it in place.

Not every resource is handled identically, and provider behavior still matters, but the concept encourages me to think about infrastructure lifecycle rather than only initial deployment. I am learning to consider what happens when a property changes, whether Terraform can update it directly, and when a replacement may be required.

Encapsulation and Cohesion

Encapsulation and cohesion become especially important as a configuration grows.

Related resources and logic should be grouped in a way that gives the configuration a clear purpose. Internal implementation details should not needlessly leak into every part of the design. A well-organized module should expose the inputs a consumer needs and return useful outputs without requiring that consumer to understand every internal resource.

This is where Terraform begins to feel less like a collection of configuration files and more like an infrastructure platform that needs deliberate architecture.

Declarative Versus Imperative Thinking

The declarative model remains one of the most important mindset changes.

With an imperative process, I describe the steps an administrator or script should perform. With a declarative configuration, I describe the desired result. Terraform evaluates the configuration, state, provider information, and existing infrastructure to determine which actions are necessary.

The language is not merely replacing portal clicks with text. It is changing how I describe infrastructure.

D.R.Y.

“Don’t Repeat Yourself” sounds straightforward, but it quickly becomes important when managing multiple environments.

Copying an entire configuration for development, test, and production might work initially. Over time, however, those copies can diverge. A correction may be implemented in one environment but forgotten in another. A new standard may require the same change in several places.

Variables, local values, reusable modules, and carefully designed environment configurations provide a better path. Reuse is not only about saving keystrokes. It helps reduce inconsistency and makes the intended pattern easier to identify.

Building the Environment Before Building Infrastructure

The next stage of the Udemy path focuses on preparing the development environment.

That includes Visual Studio Code, Terraform extensions, the Azure CLI, Git, Chocolatey, and a structured source folder.

This section may not produce an Azure resource, but it establishes the workspace in which all later Terraform development occurs.

Visual Studio Code provides the editor. Terraform tooling improves the experience of working with HCL. The Azure CLI supports interaction with Azure and authentication workflows. Git introduces version history and change management. Chocolatey helps with package installation on Windows. The source folder gives the project an intentional home rather than leaving files scattered across temporary directories.

There is a practical governance lesson here. Infrastructure as Code should be treated as code from the beginning.

  • Where configuration files are stored
  • How changes are tracked
  • Which files should not enter source control
  • How naming conventions are applied
  • How environments are separated
  • How sensitive information is protected
  • How another person could understand the project structure

A successful deployment is only one part of the outcome. The configuration should also be maintainable, reviewable, and understandable.

Moving into HCL

Once the foundation is established, the learning path moves into HCL and Terraform’s core syntax.

This is where the difference between the two learning experiences becomes most noticeable. The course is not treating HCL as syntax that I simply need to copy. It is breaking the language into a sequence of connected concepts.

The progression includes creating a resource, understanding the Terraform workflow, defining required providers, using variables and outputs, performing string interpolation, referencing resource attributes, and organizing values for different environments. It then continues into types, validation, sensitivity, workspaces, modules, and module encapsulation.

Rather than seeing one large configuration and trying to understand it all at once, I am learning what each language feature contributes to the design.

Variables Are Interfaces, Not Just Convenience Values

At first, an input variable can appear to be a simple replacement for a hardcoded string. Instead of repeating an Azure region or environment name throughout the configuration, I can define the value once and reference it where needed.

That is useful, but it is only the beginning.

An input variable creates an interface through which a configuration or module can receive information. Its definition can include a type, description, default value, sensitivity designation, and validation rules.

These elements turn variables into a form of governance.

A type describes the shape of an acceptable value. Validation can reject an input that does not meet the configuration’s requirements. A description helps another person understand the variable’s purpose. A sensitive designation limits ordinary display of the value, although it does not eliminate the need to protect state and other artifacts.

HashiCorp’s documentation describes input variables as a way for module consumers to customize behavior without changing the module’s source code. It also shows how validation can restrict acceptable values. Read HashiCorp’s input-variable documentation.

This is an important step beyond copying a sample configuration. The question is no longer simply, “What value do I place here?” It becomes, “What is the supported interface for this configuration, and how should invalid input be handled?”

Locals and Outputs Clarify Intent

Local values and outputs serve different purposes, but both can make a configuration easier to understand.

A local value can give a meaningful name to an expression or derived value used within the configuration. This can reduce repetition and make the logic more readable.

An output exposes information after Terraform evaluates or applies the configuration. That information may be useful to a person, another module, or a later automation stage.

  • Input variables accept information.
  • Local values organize internal expressions and reusable values.
  • Resource attributes represent information returned by managed resources.
  • Outputs expose selected results.

Learning these concepts individually helps me see the configuration as a flow of information rather than a static collection of blocks.

Value Selection and Environment Configuration

The course also explores input-variable files, value-selection order, and the use of variables for environment configuration.

This is where dev, test, and production begin to become more than three copied folders.

A reusable configuration can define a common infrastructure pattern while environment-specific values supply the appropriate names, sizes, locations, or other approved differences. That does not mean every environmental difference should become an unrestricted variable. Too much flexibility can make a configuration difficult to understand or govern.

The objective is controlled reuse.

Understanding how Terraform selects values is especially important because a value can potentially come from more than one place. If I do not know which source has precedence, I can misunderstand why a plan contains a particular value.

The lesson is broader than Terraform syntax. When an infrastructure deployment produces an unexpected result, I need to trace the value back to its source rather than assume the resource block alone explains it.

Sensitive Values Require Careful Thinking

The learning path includes sensitive inputs and outputs, an area that connects directly to security and governance.

Marking a value as sensitive can help prevent it from appearing in ordinary Terraform output. It does not transform the value into a secret-management system, and it does not make every artifact containing that value safe.

State may contain sensitive information. Provider behavior matters. Logs, plan files, environment variables, automation platforms, and source-control practices all need to be considered.

This reinforces a principle I have seen throughout Microsoft 365 security and governance work: a control label is not the same thing as a complete control strategy.

Workspaces and Multiple Environments

Terraform workspaces introduce another method of maintaining separate instances of state for a configuration.

They are useful to understand, but I am learning not to equate a workspace name with a complete environment architecture. Environment separation also involves identity, access, subscriptions, state storage, approval processes, configuration boundaries, and operational ownership.

The important lesson is to understand what a Terraform workspace separates and what it does not.

This is another benefit of the slower, language-first path. Instead of treating workspaces as a command to memorize, I can place them within the larger question of how environments should be isolated and managed.

Modules Bring the Earlier Concepts Together

Modules are where many of the previous lessons begin to converge.

A module can group related resources into a reusable infrastructure component. Input variables define its supported interface. Local values can organize its internal logic. Outputs expose selected results. Encapsulation hides unnecessary implementation details from the module consumer.

A module should not merely place a large configuration inside another folder. A thoughtful module represents a coherent infrastructure capability.

For example, a module might represent a standardized service pattern rather than one isolated resource. Its value comes from encoding approved decisions and making those decisions repeatable.

  • What responsibility belongs inside this module?
  • Which values should callers be allowed to change?
  • Which standards should remain fixed?
  • Which outputs are genuinely useful?
  • How should provider and version requirements be expressed?
  • Is the module cohesive, or has it accumulated unrelated responsibilities?

These are architecture questions, not just syntax questions.

How My Learning Method Is Changing

My physical training setup still supports the watch-then-implement rhythm I described previously. I use Udemy Business, an iPad, AirPlay, a secondary basement television, and my nearby home lab to separate initial comprehension from hands-on implementation.

What has changed is the material I am implementing.

Earlier sessions were often centered on an outcome: authenticate to Azure, define a provider, create resources, review the plan, and apply the configuration.

The current path encourages smaller exercises:

  1. Understand one language or design concept.
  2. Type the configuration myself.
  3. Format and validate it.
  4. Review how values move through the configuration.
  5. Deliberately change an input.
  6. Inspect the resulting plan.
  7. Introduce an error and read the response.
  8. Refactor repeated logic.
  9. Explain why the revised structure is better.
  10. Commit the meaningful change to source control.

This method produces fewer dramatic screenshots, but it builds a stronger mental model.

From Knowing the Commands to Understanding the Configuration

I am still using the familiar Terraform workflow:

  • terraform init
  • terraform plan
  • terraform apply
  • terraform destroy

Those commands remain important, but they are no longer the entire story.

The real learning is moving into the configuration itself. I am beginning to understand how inputs are selected, how references connect resources, how outputs expose information, how types and validation constrain values, how workspaces affect state, and how modules create reusable boundaries.

The first course helped me answer, “Can I use Terraform to deploy something into Azure?”

The current course is helping me answer more demanding questions:

  • Can I explain why the configuration is structured this way?
  • Can I modify it without copying large blocks of code?
  • Can I determine where a value originated?
  • Can I protect sensitive information appropriately?
  • Can I support more than one environment?
  • Can I package the pattern into a reusable module?
  • Can another person understand and safely use what I created?

That is the difference between successfully following a demonstration and beginning to develop an engineering skill.

Final Thoughts

My initial YouTube training gave me momentum. It connected Terraform to Azure and showed me the practical value of Infrastructure as Code.

The Udemy learning path is now adding depth to that experience. By slowing down and beginning with IaC principles, environment preparation, HCL syntax, variables, outputs, value-selection behavior, workspaces, and modules, it is helping me understand the system beneath the deployment.

Both approaches have value.

A results-first course can provide motivation and context. A structured language-first path can fill in the conceptual gaps that appear once the examples become more complex. In my case, the combination is proving more valuable than either approach would have been alone.

I am not trying to memorize every HCL feature or rush toward increasingly complex Azure deployments. I am trying to build a framework that will let me read, write, troubleshoot, evaluate, and eventually design Terraform configurations with confidence.

That may be the more important learning path.

Related Reading

References