Skip to main content
In this lesson we’ll write real Terraform configuration using HCL (HashiCorp Configuration Language). Up to now we covered what Terraform is and how it works conceptually. Now we’ll move into the practical part: writing the code that describes your infrastructure. If you’re new to Terraform, don’t worry — we’ll build examples step by step and explain every line. Once you grasp the basic pattern below, you’ll be able to read and author most Terraform configurations. Core pattern for Terraform configurations
  1. Tell Terraform which cloud or platform to talk to (provider block).
  2. Define the resources you want to create (resource blocks).
  3. Provide details about each resource (arguments inside resource blocks).
Basic provider + resource example
This single file contains everything Terraform needs to create a resource group in Azure:
  • The provider block tells Terraform how to communicate with Azure.
  • The resource block describes the resource group and its required arguments (name and location).
For the AzureRM provider the features {} block is required. It enables provider-default capabilities even when left empty.
Terraform’s three-command workflow After writing HCL, Terraform typically follows this workflow: Other helpful commands: terraform validate to check syntax and terraform fmt to format files. The code describes desired infrastructure; Terraform commands perform the actual changes. Provider block explained The provider tells Terraform how to communicate with an external system (here, Azure Resource Manager).
  • provider "azurerm" selects the AzureRM provider plugin.
  • features {} is required by the AzureRM provider even when left empty.
  • You can set subscription_id directly, or supply it via environment variables (for example, ARM_SUBSCRIPTION_ID) or variables for cleaner separation of secrets and configuration.
Resource block and arguments A resource block declares a resource Terraform should manage. The general form: resource “<PROVIDER_RESOURCE_TYPE>” “<LOCAL_NAME>” { arguments… }
  • <PROVIDER_RESOURCE_TYPE> — provider-specific resource type (e.g., azurerm_storage_account).
  • <LOCAL_NAME> — logical name used inside Terraform for referencing this resource (not seen by Azure).
  • Inside the block: key-value arguments describe how to create or configure the resource.
Example: storage account with hard-coded values
We use hard-coded values at first to make each argument obvious. Later you’ll replace these with variables and references for reusability. Required vs optional arguments Always consult the Terraform Registry documentation for each resource to determine required and optional arguments. Examples:
  • Resource group: name and location are required.
  • Storage account: name, resource_group_name, location, account_tier, and account_replication_type are required. Many other settings (network rules, tags, etc.) are optional.
Minimal resource group example
Example: storage account referencing a resource group
Using Visual Studio Code for Terraform Before you start: Example az account show output (truncated)
Project layout tips:
  • Create a folder for your code, e.g., first-code.
  • Start with a main.tf file. For larger projects split code into multiple .tf files and modules.
Practical walkthrough: create a resource group Create a main.tf file with the following:
Save the file before running Terraform commands — many editors show unsaved changes with an indicator (a dot). Initialize the working directory
Sample terraform init output (truncated)
Notes:
  • terraform init downloads provider binaries into the local .terraform directory.
  • The provider binary downloaded depends on your OS/architecture (e.g., darwin_arm64, windows_amd64).
Plan the deployment
terraform plan converts your HCL into an execution plan and shows what will be added, changed, or destroyed — without making changes. Apply the changes
Example interactive terraform apply output (truncated)
State file and best practices Terraform stores the mapping of your configuration to real cloud resources in a state file (default: terraform.tfstate in the working directory). This file is how Terraform tracks resources it manages.
The state file may contain sensitive information (IDs, attributes, secrets). Do not commit terraform.tfstate to version control. For teams, use a remote backend (for example, Azure Storage, S3, or Terraform Cloud) to store state securely and enable state locking.
Important behavior: drift between Terraform and cloud changes If you modify resources directly in the cloud (for example, adding tags in the Azure Portal), Terraform will detect those differences the next time you run terraform plan or terraform apply. If the portal change is not reflected in your HCL, Terraform will attempt to reconcile the real state to match your code — which may overwrite or remove the manual change. Example: adding a tag via Azure Portal You can add a tag to the resource group in the portal. The Portal shows the edit UI:
A screenshot of the Microsoft Azure portal showing the Resource Manager > Resource groups page with a list of resource groups. The right side has an "Edit tags" panel open for the resource group named kodekloud-tf-rg.
If you then run terraform plan, Terraform will show changes required to make the resource match the configuration in your HCL (for example, it may propose to remove or change that tag). This behavior demonstrates Terraform’s desired-state model: Terraform will always aim to make the real infrastructure match the declared code. Wrap-up You now know the basic HCL pattern (provider → resource → arguments) and the essential Terraform workflow (init, plan, apply). Next topics to continue learning:
  • Variables and outputs (parameterize your configurations)
  • State backends and locking (secure, shareable state)
  • Modules (reusable infrastructure components)
  • Best practices for project structure and secrets management
References

Watch Video