How to Install Terraform on Windows Server 2025

Terraform, developed by HashiCorp, is an open-source infrastructure-as-code (IaC) tool that lets you define, provision, and manage cloud and on-premises resources using a declarative configuration language called HCL (HashiCorp Configuration Language). On Windows Server 2025, Terraform integrates well with PowerShell workflows and enables teams to automate infrastructure provisioning across AWS, Azure, Google Cloud, and hundreds of other providers. This tutorial walks you through downloading and installing Terraform, configuring provider credentials, writing your first configuration, and executing the core Terraform workflow from a Windows Server 2025 environment.

Prerequisites

  • Windows Server 2025 with PowerShell 5.1 or PowerShell 7.x
  • Administrator privileges on the server
  • Internet access to download the Terraform binary and provider plugins
  • An AWS account and/or Azure subscription (for provider configuration steps)
  • Basic familiarity with command-line tools and text editors

Step 1: Download Terraform from HashiCorp

Terraform is distributed as a single binary ZIP archive for Windows. You can download it directly from releases.hashicorp.com using PowerShell’s Invoke-WebRequest cmdlet. Always verify the checksum after downloading to ensure file integrity.

# Set the version you want to install
$TerraformVersion = "1.8.5"
$ZipUrl = "https://releases.hashicorp.com/terraform/$TerraformVersion/terraform_${TerraformVersion}_windows_amd64.zip"
$ChecksumUrl = "https://releases.hashicorp.com/terraform/$TerraformVersion/terraform_${TerraformVersion}_SHA256SUMS"
$DownloadPath = "$env:TEMPterraform_${TerraformVersion}_windows_amd64.zip"

# Download the ZIP
Write-Host "Downloading Terraform $TerraformVersion..."
Invoke-WebRequest -Uri $ZipUrl -OutFile $DownloadPath -UseBasicParsing

# Download and verify checksum
$ChecksumFile = "$env:TEMPterraform_SHA256SUMS.txt"
Invoke-WebRequest -Uri $ChecksumUrl -OutFile $ChecksumFile -UseBasicParsing

$ExpectedHash = (Get-Content $ChecksumFile | Where-Object { $_ -match "windows_amd64.zip" }) -split "s+" | Select-Object -First 1
$ActualHash   = (Get-FileHash -Path $DownloadPath -Algorithm SHA256).Hash

if ($ActualHash -ieq $ExpectedHash) {
    Write-Host "Checksum verified successfully."
} else {
    Write-Error "Checksum mismatch! Aborting installation."
    exit 1
}

Step 2: Extract Terraform and Add to PATH

HashiCorp recommends placing Terraform at C:HashiCorpTerraform. After extraction, you add that directory to the system PATH so Terraform is accessible from any PowerShell or Command Prompt session without specifying the full path.

# Create the destination directory
$InstallDir = "C:HashiCorpTerraform"
New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null

# Extract the binary
Expand-Archive -Path $DownloadPath -DestinationPath $InstallDir -Force

# Add to system PATH permanently (requires elevated PowerShell)
$CurrentPath = [System.Environment]::GetEnvironmentVariable("Path", "Machine")
if ($CurrentPath -notlike "*$InstallDir*") {
    [System.Environment]::SetEnvironmentVariable(
        "Path",
        "$CurrentPath;$InstallDir",
        "Machine"
    )
    Write-Host "Added $InstallDir to system PATH."
} else {
    Write-Host "$InstallDir already in PATH."
}

# Reload PATH in current session
$env:Path = [System.Environment]::GetEnvironmentVariable("Path", "Machine")

# Verify installation
terraform version

Expected output should resemble: Terraform v1.8.5 on windows_amd64. If you see this, the binary is installed and accessible.

Step 3: Configure Provider Credentials

Terraform providers authenticate using environment variables, configuration files, or integrated credential managers. Setting environment variables at the system level (or session level for testing) is the most portable approach on Windows Server 2025.

AWS Credentials

# Set AWS credentials as environment variables for the current session
$env:AWS_ACCESS_KEY_ID     = "AKIAIOSFODNN7EXAMPLE"
$env:AWS_SECRET_ACCESS_KEY = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
$env:AWS_DEFAULT_REGION    = "us-east-1"

# To persist across sessions (system-level, all users):
[System.Environment]::SetEnvironmentVariable("AWS_ACCESS_KEY_ID",     "AKIAIOSFODNN7EXAMPLE",            "Machine")
[System.Environment]::SetEnvironmentVariable("AWS_SECRET_ACCESS_KEY", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", "Machine")
[System.Environment]::SetEnvironmentVariable("AWS_DEFAULT_REGION",    "us-east-1",                       "Machine")

Azure Credentials (Service Principal)

# Azure provider uses ARM_* environment variables for service principal auth
[System.Environment]::SetEnvironmentVariable("ARM_CLIENT_ID",       "00000000-0000-0000-0000-000000000001", "Machine")
[System.Environment]::SetEnvironmentVariable("ARM_CLIENT_SECRET",   "YourServicePrincipalSecret",           "Machine")
[System.Environment]::SetEnvironmentVariable("ARM_TENANT_ID",       "00000000-0000-0000-0000-000000000002", "Machine")
[System.Environment]::SetEnvironmentVariable("ARM_SUBSCRIPTION_ID", "00000000-0000-0000-0000-000000000003", "Machine")

Step 4: Write Your First Terraform Configuration

Terraform configurations live in .tf files within a working directory. A minimal configuration consists of a terraform block declaring required providers, a provider block configuring the provider, and one or more resource blocks defining infrastructure objects. Create a new directory and a main.tf file:

New-Item -ItemType Directory -Path "C:TerraformProjectsdemo" -Force | Out-Null
Set-Location "C:TerraformProjectsdemo"

# Write main.tf
@"
terraform {
  required_version = ">= 1.8.0"

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

resource "aws_s3_bucket" "demo_bucket" {
  bucket = "my-demo-bucket-ws2025-unique-$(Get-Random)"

  tags = {
    Environment = "Demo"
    ManagedBy   = "Terraform"
    Server      = "WindowsServer2025"
  }
}

output "bucket_name" {
  value = aws_s3_bucket.demo_bucket.bucket
}
"@ | Set-Content -Path "main.tf" -Encoding UTF8

Write-Host "main.tf created."

Step 5: Run the Terraform Workflow — Init, Plan, Apply, Destroy

The core Terraform workflow consists of four commands that you run in sequence from the configuration directory.

Set-Location "C:TerraformProjectsdemo"

# terraform init: downloads providers declared in required_providers
# Creates .terraform/ directory and .terraform.lock.hcl
terraform init

# terraform plan: shows what Terraform will create/update/destroy
# Reads current state vs desired configuration
terraform plan -out="tfplan.binary"

# terraform apply: applies the plan, creates real infrastructure
# -auto-approve skips the interactive confirmation prompt
terraform apply "tfplan.binary"

# After testing, destroy all resources created by this configuration
terraform destroy -auto-approve

The terraform init step downloads the AWS provider plugin (or whichever providers are declared) into the .terraform/providers subdirectory and generates or updates the .terraform.lock.hcl dependency lock file. Committing .terraform.lock.hcl to source control ensures that all team members and CI pipelines use the same provider versions.

Step 6: Understanding the Lock File and Local State

After running terraform init, two important files are created: .terraform.lock.hcl and (after your first apply) terraform.tfstate. The lock file pins exact provider versions and their checksums. The state file records the current real-world state of your managed resources. For production workloads, move state to a remote backend.

# View the lock file
Get-Content ".terraform.lock.hcl"

# View local state (JSON format — never edit manually)
Get-Content "terraform.tfstate" | ConvertFrom-Json | ConvertTo-Json -Depth 10

Step 7: Configure Remote State (S3 or Azure Blob)

Storing Terraform state remotely enables team collaboration and prevents accidental state loss. Below are backend configurations for both AWS S3 and Azure Blob Storage.

Remote State on AWS S3

# Add to main.tf terraform block:
@"
terraform {
  required_version = ">= 1.8.0"

  backend "s3" {
    bucket         = "my-tfstate-bucket"
    key            = "demo/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-lock-table"  # for state locking
  }

  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}
"@ | Set-Content -Path "backend.tf" -Encoding UTF8

# Re-initialize to migrate state to S3
terraform init -reconfigure

Remote State on Azure Blob Storage

@"
terraform {
  backend "azurerm" {
    resource_group_name  = "tfstate-rg"
    storage_account_name = "tfstatesa2025"
    container_name       = "tfstate"
    key                  = "demo.terraform.tfstate"
  }
}
"@ | Set-Content -Path "backend.tf" -Encoding UTF8

terraform init -reconfigure

Step 8: Useful Day-to-Day Terraform Commands

# Show current state in human-readable form
terraform show

# List all resources tracked in state
terraform state list

# Import an existing resource into state (without recreating it)
terraform import aws_s3_bucket.demo_bucket existing-bucket-name

# Refresh state to match real-world resources
terraform refresh

# Validate configuration syntax without connecting to providers
terraform validate

# Format all .tf files to canonical style
terraform fmt -recursive

# Output defined output values
terraform output bucket_name

Conclusion

You have successfully installed Terraform on Windows Server 2025, configured AWS and Azure provider credentials using environment variables, written and executed a complete Terraform workflow, and learned how to manage state both locally and remotely. Terraform’s declarative model makes it straightforward to version-control your infrastructure alongside application code, enabling repeatable and auditable deployments. As a next step, explore Terraform modules to encapsulate reusable infrastructure patterns, and integrate terraform plan into your CI/CD pipelines so that infrastructure changes are reviewed before being applied to production environments.