Terraform - Download & Configure on Windows

About Terraform Terraform is an Infrastructure as Code (IaC) tool from HashiCorp, and it is an open-source, cross-platform and multi-cloud infrastructure deployment tool. It uses HashiCorp Configuration Language (HCL) and supports all popular cloud service providers. Terraform Installation Terraform comes as a single binary in a zip archive, you need to download it from the terraform official download page  , extract the archive and you can use it without having it installed and you can also use chocolate  package provider to download the terraform. To use the binary globally you need to set the binary location to the PATH. Alternatively, the PowerShell script below does all these steps for you and make it ready to run your terraform deployments… Function Install-Terraform { # Ensure to run the function with administrator privilege if (-not (New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent())).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Host -ForegroundColor Red -Object "!!! Please run as Administrator !!!"; return } # Terrafrom download Url $Url = 'https://www.terraform.io/downloads.html' # Local path to download the terraform zip file $DownloadPath = 'C:\Terraform\' # Reg Key to set the persistent PATH $RegPathKey = 'HKLM:\System\CurrentControlSet\Control\Session Manager\Environment' # Create the local folder if it doesn't exist if ((Test-Path -Path $DownloadPath) -eq $false) { $null = New-Item -Path $DownloadPath -ItemType Directory -Force } # Download the Terraform exe in zip format $Web = Invoke-WebRequest -Uri $Url $FileInfo = $Web.Links | Where-Object href -match windows_amd64 $DownloadLink = $FileInfo.href $FileName = Split-Path -Path $DownloadLink -Leaf $DownloadFile = [string]::Concat( $DownloadPath, $FileName ) Invoke-RestMethod -Method Get -Uri $DownloadLink -OutFile $DownloadFile # Extract & delete the zip file Expand-Archive -Path $DownloadFile -DestinationPath $DownloadPath -Force Remove-Item -Path $DownloadFile -Force # Setting the persistent path in the registry if it is not set already if ($DownloadPath -notin $($ENV:Path -split ';')) { $PathString = (Get-ItemProperty -Path $RegPathKey -Name PATH).Path $PathString += ";$DownloadPath" Set-ItemProperty -Path $RegPathKey -Name PATH -Value $PathString # Setting the path for the current session $ENV:Path += ";$DownloadPath" } # Verify the download Invoke-Expression -Command "terraform version" } You can run this function any number of times, if terraform is not configured it will download and configure the path, if it is already there it will replace/upgrade with the latest version. You can visit terrafrom docs  to start with terraform. To start with Azure deployments using terraform you can visit Microsoft Docs. 

Deploy A Simple Windows VM (101-vm-simple-windows) Using Terraform

This terraform configuration will deploy a simple Windows VM on Azure cloud, and this is a conversion of 101-vm-simple-windows  from azure_quickstart_templates.  This configuration consists of two .tf files (variables.tf and main.tf) and deploy a single vm with the following resources… Visualized in ARMVIZ Deployed resources Configuration variables.tf Variables are separated from the main configuration, and this configuration accepts the variable below… provider "azurerm" { version = "=1.36.0" } variable "resourceGroupName" { type = string description = "Resource Group for this deployment." } variable "location" { type = string description = "Location for all resources" } variable "adminUsername" { type = string description = "Username for the Virtual Machine." } variable "adminPassword" { type = string description = "Password for the Virtual Machine." } variable "dnsLabelPrefix" { type = string description = "Unique DNS Name for the Public IP used to access the Virtual Machine." } variable "windowsOSVersion" { type = list default = ["2016-Datacenter","2008-R2-SP1","2012-Datacenter","2012-R2-Datacenter","2016-Nano-Server","2016-Datacenter-with-Containers","2019-Datacenter"] description = "The Windows version for the VM. This will pick a fully patched image of this given Windows version." } variable "vmSize" { type = string default = "Standard_A2_v2" description = "Size of the virtual machine." } main.tf This file contains the actual configuration to create a simple Windows VM… # Declaring the local variables locals { storageAccountName = lower(join("", ["sawinvm", random_string.asaname-01.result])) nicName = "myVMNic" addressPrefix = "" subnetName = "Subnet" subnetPrefix = "" publicIPAddressName = "myPublicIP" vmName = "SimpleWinVM" virtualNetworkName = "MyVNET" networkSecurityGroupName = "default-NSG" osDiskName = join("",[local.vmName, "_OsDisk_1_", lower(random_string.avmosd-01.result)]) } # Generating the random string to create a unique storage account resource "random_string" "asaname-01" { length = 16 special = "false" } # Generating the random string to create a unique os disk resource "random_string" "avmosd-01" { length = 32 special = "false" } # Resource Group resource "azurerm_resource_group" "arg-01" { name = var.resourceGroupName location = var.location } # Storage Account resource "azurerm_storage_account" "asa-01" { name = local.storageAccountName resource_group_name = azurerm_resource_group.arg-01.name location = azurerm_resource_group.arg-01.location account_replication_type = "LRS" account_tier = "Standard" } # Public IP resource "azurerm_public_ip" "apip-01" { name = local.publicIPAddressName resource_group_name = azurerm_resource_group.arg-01.name location = azurerm_resource_group.arg-01.location allocation_method = "Dynamic" domain_name_label = var.dnsLabelPrefix } # Network Security Group with allow RDP rule resource "azurerm_network_security_group" "ansg-01" { name = local.networkSecurityGroupName resource_group_name = azurerm_resource_group.arg-01.name location = azurerm_resource_group.arg-01.location security_rule { name = "default-allow-3389" priority = 1000 access = "Allow" direction = "Inbound" destination_port_range = 3389 protocol = "Tcp" source_port_range = "*" source_address_prefix = "*" destination_address_prefix = "*" } } # Virtual Network resource "azurerm_virtual_network" "avn-01" { name = local.virtualNetworkName resource_group_name = azurerm_resource_group.arg-01.name location = azurerm_resource_group.arg-01.location address_space = [local.addressPrefix] } # Subnet resource "azurerm_subnet" "as-01" { name = local.subnetName resource_group_name = azurerm_resource_group.arg-01.name virtual_network_name = azurerm_virtual_network.avn-01.name address_prefix = local.subnetPrefix } # Associate the subnet with NSG resource "azurerm_subnet_network_security_group_association" "asnsga-01" { subnet_id = azurerm_subnet.as-01.id network_security_group_id = azurerm_network_security_group.ansg-01.id } # Network Interface Card resource "azurerm_network_interface" "anic-01" { name = local.nicName resource_group_name = azurerm_resource_group.arg-01.name location = azurerm_resource_group.arg-01.location ip_configuration { name = "ipconfig1" private_ip_address_allocation = "Dynamic" public_ip_address_id = azurerm_public_ip.apip-01.id subnet_id = azurerm_subnet.as-01.id } } # Virtual Machine resource "azurerm_virtual_machine" "avm-01" { name = local.vmName resource_group_name = azurerm_resource_group.arg-01.name location = azurerm_resource_group.arg-01.location vm_size = var.vmSize network_interface_ids = [azurerm_network_interface.anic-01.id] os_profile { computer_name = local.vmName admin_username = var.adminUsername admin_password = var.adminPassword } storage_image_reference { publisher = "MicrosoftWindowsServer" offer = "WindowsServer" sku = var.windowsOSVersion[0] version = "latest" } storage_os_disk { name = local.osDiskName create_option = "FromImage" } storage_data_disk { name = "Data" disk_size_gb = 1023 lun = 0 create_option = "Empty" } os_profile_windows_config { provision_vm_agent = true } boot_diagnostics { enabled = true storage_uri = azurerm_storage_account.asa-01.primary_blob_endpoint } } # Print virtual machine dns name output "hostname" { value = azurerm_public_ip.apip-01.fqdn } Deploy the configuration Authentication Please use AzureCli to authenticate to your Azure cloud environment, terraform can use the same session to deploy the resources. Check here  for alternate methods of authentication. Initialization First time you need to initialize the configuration directory, where it will download the necessary plugins to deploy the current configuration, to initialize you need to run the command below… terraform init Plan Terraform will check for the syntax errors and generates the execution plan, you can also save this plan for your future deployments terraform plan Apply To apply the configuration run the command below… terraform apply