惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
Cyberwarzone
Cyberwarzone
The GitHub Blog
The GitHub Blog
云风的 BLOG
云风的 BLOG
P
Proofpoint News Feed
小众软件
小众软件
Recent Announcements
Recent Announcements
博客园 - 三生石上(FineUI控件)
Security Archives - TechRepublic
Security Archives - TechRepublic
W
WeLiveSecurity
Cloudbric
Cloudbric
博客园 - 司徒正美
美团技术团队
N
News and Events Feed by Topic
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
PCI Perspectives
PCI Perspectives
宝玉的分享
宝玉的分享
H
Help Net Security
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Google DeepMind News
Google DeepMind News
Help Net Security
Help Net Security
Last Week in AI
Last Week in AI
S
Schneier on Security
N
News | PayPal Newsroom
B
Blog RSS Feed
L
LINUX DO - 最新话题
T
Troy Hunt's Blog
S
Secure Thoughts
雷峰网
雷峰网
aimingoo的专栏
aimingoo的专栏
L
Lohrmann on Cybersecurity
G
Google Developers Blog
Microsoft Azure Blog
Microsoft Azure Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
T
Tenable Blog
S
Securelist
L
LangChain Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
I
InfoQ
H
Heimdal Security Blog
Cisco Talos Blog
Cisco Talos Blog
F
Full Disclosure
Y
Y Combinator Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
K
Kaspersky official blog
T
Tailwind CSS Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
阮一峰的网络日志
阮一峰的网络日志
C
Cisco Blogs

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Deploying Infrastructure to Azure Using HCP Terraform and GitHub VCS
Bernard Chika Uwaezuoke · 2026-06-05 · via DEV Community

INTRODUCTION

Every Cloud Engineer eventually reaches the same point.
At first, running Terraform from your laptop feels exciting. You write your .tf files, run:

terraform init
terraform plan
terraform apply

and Azure resources appear like magic.

But as projects grow, that approach becomes risky.
What happens when another engineer needs to review your changes?
What happens when the state file is sitting on someone’s laptop?
What happens when a junior engineer accidentally applies changes to production without approval?

That is where HCP Terraform and GitHub VCS integration come in.
In this project, I will walk you through how to deploy Azure infrastructure using Terraform code stored in GitHub, with HCP Terraform handling remote runs, state management, plan review, and controlled apply operations.

As a Cloud Engineer and technical instructor, this is the kind of workflow I encourage students and teams to learn early because it reflects how real infrastructure should be managed: version-controlled, reviewable, auditable, and repeatable.

HCP Terraform supports a VCS-driven workflow where it fetches Terraform configuration from a connected repository and automatically starts plan and apply operations when changes are pushed. This allows GitHub to act as the source of truth for infrastructure code.

Project Goal

We are going to deploy basic Azure infrastructure using:

  • Azure as the cloud provider
  • Terraform as the Infrastructure as Code tool
  • GitHub as the version control system
  • HCP Terraform as the remote execution and state management platform

The deployment will create:

  • Azure Resource Group
  • Virtual Network
  • Subnet
  • Network Security Group
  • Public IP
  • Network Interface
  • Linux Virtual Machine

Architecture Overview

Architectural Overview

In simple terms:

  1. We write Terraform code locally.
  2. We push the code to GitHub.
  3. HCP Terraform detects the change.
  4. HCP Terraform runs terraform plan.
  5. After approval, HCP Terraform runs terraform apply.
  6. Azure resources are deployed.

This is a professional DevOps workflow because no one is manually creating resources in the Azure portal, and no one is running production infrastructure from their laptop (Minimum DevOps requirements).

Prerequisites

Before starting, make sure you have:

  1. An Azure subscription
  2. A GitHub account
  3. An HCP Terraform account
  4. Terraform installed locally
  5. Azure CLI installed locally
  6. Basic knowledge of Git and Terraform

You also need permission to create resources in Azure and permission to create or use a service principal click here to know more.

Here We Go!

Step 1: Create a GitHub Repository

  • Create a new GitHub repository.
    azure-hcp-terraform-vcs

  • Clone the repository locally
    git clone https://github.com/YOUR_USERNAME/azure-hcp-terraform-vcs.git

  • Switch to the cloned repository
    cd azure-hcp-terraform-vcs

Create a repo

Creating a Repo in GitHub

For details on how to create a GitHub repository, click here

  • Create the Terraform project structure azure-hcp-terraform-vcs/

├── main.tf
├── variables.tf
├── outputs.tf
├── providers.tf
├── terraform.tfvars
└── README.md

Enter fullscreen mode Exit fullscreen mode

Project Structure

The above structure is simple enough for beginners to understand, but also professional enough to build on later.

Step 2: Configure the Azure Provider

  • Create providers.tf.
terraform {
  required_version = ">= 1.6.0"

  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 4.0"
    }
  }
}

provider "azurerm" {
  features {}
}

Enter fullscreen mode Exit fullscreen mode

This tells Terraform to use the AzureRM provider.
The features {} block is required by the AzureRM provider.

Step 3: Create Terraform Variables

  • Create variables.tf.
variable "resource_group_name" {
  description = "Name of the Azure Resource Group"
  type        = string
}

variable "location" {
  description = "Azure region where resources will be deployed"
  type        = string
}

variable "vnet_name" {
  description = "Name of the Virtual Network"
  type        = string
}

variable "vnet_address_space" {
  description = "Address space for the Virtual Network"
  type        = list(string)
}

variable "subnet_name" {
  description = "Name of the subnet"
  type        = string
}

variable "subnet_address_prefixes" {
  description = "Address prefixes for the subnet"
  type        = list(string)
}

variable "vm_name" {
  description = "Name of the Linux Virtual Machine"
  type        = string
}

variable "admin_username" {
  description = "Admin username for the virtual machine"
  type        = string
}

variable "public_key" {
  description = "SSH public key content for the virtual machine"
  type        = string
}


Enter fullscreen mode Exit fullscreen mode

Important Note

In HCP Terraform, avoid using local file paths like this:
file("C:/Users/USER/.ssh/azure_rsa.pub")
That path exists on your laptop, not inside the HCP Terraform remote execution environment.
Instead, paste the actual public key content into an HCP Terraform variable and check the sensitive atribute box.

Step 4: Create the Azure Infrastructure Code

  • Create main.tf.
resource "azurerm_resource_group" "rg" {
  name     = var.resource_group_name
  location = var.location
}

resource "azurerm_virtual_network" "vnet" {
  name                = var.vnet_name
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  address_space       = var.vnet_address_space
}

resource "azurerm_subnet" "subnet" {
  name                 = var.subnet_name
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.vnet.name
  address_prefixes     = var.subnet_address_prefixes
}

resource "azurerm_network_security_group" "nsg" {
  name                = "${var.vm_name}-nsg"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  security_rule {
    name                       = "Allow-SSH"
    priority                   = 1001
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "22"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }
}

resource "azurerm_public_ip" "public_ip" {
  name                = "${var.vm_name}-public-ip"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  allocation_method   = "Static"
  sku                 = "Standard"
}

resource "azurerm_network_interface" "nic" {
  name                = "${var.vm_name}-nic"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  ip_configuration {
    name                          = "internal"
    subnet_id                     = azurerm_subnet.subnet.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = azurerm_public_ip.public_ip.id
  }
}

resource "azurerm_network_interface_security_group_association" "nsg_association" {
  network_interface_id      = azurerm_network_interface.nic.id
  network_security_group_id = azurerm_network_security_group.nsg.id
}

resource "azurerm_linux_virtual_machine" "vm" {
  name                = var.vm_name
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  size                = "Standard_D2s_v3"
  admin_username      = var.admin_username

  network_interface_ids = [
    azurerm_network_interface.nic.id
  ]

  admin_ssh_key {
    username   = var.admin_username
    public_key = var.public_key
  }

  os_disk {
    name                 = "${var.vm_name}-osdisk"
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "Canonical"
    offer     = "ubuntu-24_04-lts"
    sku       = "server"
    version   = "latest"
  }
}

Enter fullscreen mode Exit fullscreen mode

This code builds a simple Azure VM environment with networking, security, and SSH access.
This is a good starter project because it touches the core Azure infrastructure components: compute, networking, identity, and security.

Step 5: Create Outputs

  • Create outputs.tf.
output "resource_group_name" {
  description = "The name of the created resource group"
  value       = azurerm_resource_group.rg.name
}

output "public_ip_address" {
  description = "The public IP address of the virtual machine"
  value       = azurerm_public_ip.public_ip.ip_address
}

output "vm_name" {
  description = "The name of the virtual machine"
  value       = azurerm_linux_virtual_machine.vm.name
}

Enter fullscreen mode Exit fullscreen mode

Outputs are useful because after deployment, HCP Terraform will show important values such as the public IP address of the VM.

Step 6: Create an Example tfvars File

  • Create terraform.tfvars
resource_group_name      = "rg-hcp-terraform-demo"
location                 = "westeurope"
vnet_name                = "vnet-hcp-demo"
vnet_address_space       = ["10.0.0.0/16"]
subnet_name              = "subnet-hcp-demo"
subnet_address_prefixes  = ["10.0.1.0/24"]
vm_name                  = "vm-hcp-demo"
admin_username           = "azureuser"
public_key               = "ssh-rsa AAAA..."

Enter fullscreen mode Exit fullscreen mode

Do not commit real secrets or sensitive values into GitHub.
For public_key, use the content of your .pub file.
For example:
cat ~/.ssh/azure_rsa.pub
On Windows PowerShell:
Get-Content C:\Users\USER\.ssh\azure_rsa.pub
Copy the full public key content.

Step 7: Create an Azure Service Principal

HCP Terraform needs permission to deploy resources into Azure.
The common approach is to create a service principal and store its values as environment variables in HCP Terraform. Microsoft recommends using service principals for automated tools such as Terraform instead of using a fully privileged personal user account.

  • Login to Azure:
    az login

  • Confirm your subscription:
    az account show

  • Set the subscription:
    az account set --subscription "<SUBSCRIPTION_ID>"

  • Create a service principal:

az ad sp create-for-rbac \
  --name "sp-hcp-terraform-azure" \
  --role "Contributor" \
  --scopes "/subscriptions/<SUBSCRIPTION_ID>"

Enter fullscreen mode Exit fullscreen mode

The output will look similar to this:

{
  "appId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
  "displayName": "sp-hcp-terraform-azure",
  "password": "xxxxxxxxxxxxxxxxxxxx",
  "tenant": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}

Enter fullscreen mode Exit fullscreen mode

Save these values securely.
You will need them in HCP Terraform.

Step 8: Push Your Code to GitHub

  • In your working director, run:
git status
git add .
git commit -m "Initial Azure infrastructure with Terraform"
git push origin main

Enter fullscreen mode Exit fullscreen mode

At this point, GitHub becomes the source of truth for your infrastructure code.
This is a major shift from manual cloud administration to real Infrastructure as Code practice.

Push to GitHub

  • The files in the repository in GitHub Terraform Config Files in GitHub

Step 9: Create an HCP Terraform Organization

  • Go to HCP Terraform and create an organization. Click here

  • Click on Create Organization
    Select Personal
    A good organization naming pattern could be:
    peabsmart-cloud-labs

HCP Organization

  • On the left pane inside the organization click on Projects, then click on New project and enter the project detail and Click on Create. For this project, we will use Azure-Training-Projects

Create a Project

Projects in HCP Terraform help organize related workspaces, especially when you are managing multiple environments or cloud providers. HashiCorp’s HCP Terraform learning path includes project and variable set organization as part of the recommended collaboration workflow.

Step 10: Connect HCP Terraform to GitHub and Create a HCP Terraform Workspace

  • In the project you created, Click on Create a workspace HCP Terraform:

Create a workspace

  • Select Version Control Workflow

  • Choose GitHub.

Connect GitHub

  • Authorize HCP Terraform to access your GitHub account or organization.

  • Select the repository: azure-hcp-terraform-vcs

  • Click on Create

    Terraform working directory: /

    Use / if your .tf files are in the root of the repository.
    If your files are inside a folder liketerraform/, then set the working directory to: terraform

This is a common mistake. If the working directory is wrong, HCP Terraform will not find your Terraform configuration.

Complete Workspace

  • The Terraform variable page will popup

  • Enter the required variable values from terraform.tfvars file we created earlier and click Save variables.

Variables

Step 11: Add Azure Credentials to HCP Terraform

  • Go to the workspace:
    Workspace > Variables

  • Add the following as Environment Variables:
    ARM_CLIENT_ID
    ARM_CLIENT_SECRET
    ARM_SUBSCRIPTION_ID
    ARM_TENANT_ID

Use these values:

ARM_CLIENT_ID        = appId from the service principal output
ARM_CLIENT_SECRET    = password from the service principal output
ARM_SUBSCRIPTION_ID  = your Azure subscription ID
ARM_TENANT_ID        = tenant from the service principal output
Mark ARM_CLIENT_SECRET as Sensitive.

Enter fullscreen mode Exit fullscreen mode

Env Variables

Microsoft documents these Azure Terraform authentication variables as the standard service-principal environment variables for Terraform automation.

Step 12: Queue the First Terraform Plan

Once the workspace is connected to GitHub and variables are added, HCP Terraform will usually trigger a run automatically.

You can also manually queue a run:
Workspace > Runs > Queue plan
HCP Terraform will run:
terraform init
terraform plan
remotely.
This is where the value becomes clear.
The plan is no longer hidden on one person’s machine. It is visible, reviewable, and part of the deployment workflow.

runs

Step 13: Review the Plan

A good Cloud Engineer does not blindly apply infrastructure changes.
Review the plan carefully.
Look for:

  • Resources to be created
  • Resources to be modified
  • Resources to be destroyed
  • Unexpected region changes
  • Unexpected VM sizes
  • Public exposure
  • Security group rules
  • Naming mistakes

For example, if the plan says:
Plan: 8 to add, 0 to change, 0 to destroy.
that means Terraform intends to create 8 resources.
A depicted in the image below

Step 14: Apply the Infrastructure

If the plan looks correct, click: Confirm & Apply

Apply

HCP Terraform will run the apply operation remotely.
After a successful apply, you should see your outputs:

resource_group_name = rg-hcp-terraform-demo
vm_name             = vm-hcp-demo
public_ip_address   = x.x.x.x
You can now test SSH access:
ssh azureuser@<PUBLIC_IP_ADDRESS>

Enter fullscreen mode Exit fullscreen mode

  • Apply completed

Apply completed

  • Prove of authorization after review (Reviewer's Comment)

Reviewers

Step 15: Verify Resources in Azure

  • Go to the Azure portal and check:
    Resource Groups > rg-hcp-terraform-demo
    You should see:

  • Virtual Machine

  • Virtual Network

  • Subnet

  • Network Security Group

  • Public IP

  • Network Interface

  • OS Disk

As Depicted below

Azure Portal

You can also verify using Azure CLI:

az resource list \
  --resource-group rg-hcp-terraform-demo \
  --output table

Enter fullscreen mode Exit fullscreen mode

Step 16: Make a Change Through GitHub

Imagine your team wants to change the VM size.

  • Update this line in main.tf:
    size = "Standard_D2s_v3"
    Change it to:
    size = "Standard_B1s"

  • Commit and push:

git add .
git commit -m "Update VM size"
git push origin main

Enter fullscreen mode Exit fullscreen mode

HCP Terraform detects the GitHub change and starts a new run.
This is the power of VCS-driven infrastructure.
You are no longer clicking around in Azure. You are managing infrastructure through code, Git history, and controlled deployment runs.

Step 17: Destroy the Infrastructure Safely

When you are done with the demo, destroy the resources to avoid unnecessary Azure cost.

  • In HCP Terraform: Workspace > Settings > Destruction and Deletion > Queue destroy plan

Queue destroy plan

  • Confirm by typing delete
    delete

  • Review the destroy plan, then confirm.

Confirm destroy

  • Deletion Completed Deletion completed

HCP Terraform supports destroy operations from the workspace, and HashiCorp warns that deleting a workspace does not automatically destroy the infrastructure it manages. You must queue a destroy plan if you want the actual cloud resources removed.

Common Errors and How to Fix Them

Error 1: Unsupported Operator for public_key_path

Example error:
invalid HCL for variable "public_key_path" at 1,1: Unsupported operator
Cause:
You are probably passing a Windows path like this directly into HCP Terraform:
C:/Users/USER/.ssh/azure_rsa.pub
HCP Terraform runs remotely and cannot access files on your local machine.
Fix:
Use the actual public key content instead:
public_key = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQ..."

Error 2: List of String Required, But Have String

Bad value:
vnet_address_space = "[\"10.0.0.0/16\"]"
Correct value:
vnet_address_space = ["10.0.0.0/16"]
Cause:
The first example is a string. The second example is a real HCL list.

Error 3: Azure Authentication Failed

Check that these environment variables exist in HCP Terraform:
ARM_CLIENT_ID
ARM_CLIENT_SECRET
ARM_SUBSCRIPTION_ID
ARM_TENANT_ID

Also confirm that the service principal has enough permissions on the subscription or resource group.

Error 4: Terraform Cannot Find Configuration

Cause:
Your HCP Terraform workspace working directory is wrong.
If your files are here:
repo/main.tf
use:
/
If your files are here:
repo/terraform/main.tf
use:
terraform

Best Practices I Teach My Students

1. Do Not Store Secrets in GitHub

Never commit:

  • client secrets
  • passwords
  • private keys
  • .tfstate files Use HCP Terraform sensitive variables for secrets.

2. Use Git Branches and Pull Requests

For production teams, avoid pushing directly to main.
Use:
feature branch > pull request > review > merge > HCP Terraform run
This improves collaboration and prevents careless infrastructure changes.

3. Keep State Remote

One of the strongest benefits of HCP Terraform is remote state management.
Your state is not scattered across laptops.
It is centrally managed, protected, and connected to the workspace.

4. Separate Environments

For serious projects, create separate workspaces:

  • azure-dev
  • azure-staging
  • azure-prod Each workspace should have its own variables and approval controls.

5. Use Clear Naming Standards

Bad names:

  • test-rg
  • vm1
  • network-demo

Better names:

  • rg-dev-hcp-demo-weu
  • vnet-dev-hcp-demo-weu
  • vm-dev-hcp-demo-01 Good naming helps with operations, cost tracking, troubleshooting, and governance.

Final Reflection

This project is more than just deploying a virtual machine to Azure.
It demonstrates a professional Infrastructure as Code workflow.
As a Cloud Engineer, I see Terraform as more than a provisioning tool. It is a discipline. It teaches structure, repeatability, version control, collaboration, and accountability.

As a technical instructor, I also see this workflow as one of the best ways to help students understand how modern cloud teams work. They learn that cloud engineering is not about clicking buttons in the portal. It is about building systems that are predictable, secure, reviewable, and easy to reproduce.