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

推荐订阅源

Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
D
Darknet – Hacking Tools, Hacker News & Cyber Security
N
News and Events Feed by Topic
N
News | PayPal Newsroom
SecWiki News
SecWiki News
P
Privacy International News Feed
T
Troy Hunt's Blog
Attack and Defense Labs
Attack and Defense Labs
N
News and Events Feed by Topic
L
LINUX DO - 热门话题
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Security Latest
Security Latest
AWS News Blog
AWS News Blog
S
Secure Thoughts
W
WeLiveSecurity
H
Heimdal Security Blog
T
Threat Research - Cisco Blogs
I
Intezer
Application and Cybersecurity Blog
Application and Cybersecurity Blog
S
Security @ Cisco Blogs
G
GRAHAM CLULEY
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Spread Privacy
Spread Privacy
L
Lohrmann on Cybersecurity
C
CERT Recently Published Vulnerability Notes
S
Security Affairs
Hacker News - Newest:
Hacker News - Newest: "LLM"
Google Online Security Blog
Google Online Security Blog
Cisco Talos Blog
Cisco Talos Blog
雷峰网
雷峰网
Cloudbric
Cloudbric
Y
Y Combinator Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园_首页
Hacker News: Ask HN
Hacker News: Ask HN
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Google DeepMind News
Google DeepMind News
Vercel News
Vercel News
云风的 BLOG
云风的 BLOG
Latest news
Latest news
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
D
Docker
Recent Announcements
Recent Announcements
博客园 - 【当耐特】
H
Help Net Security
博客园 - 司徒正美
TaoSecurity Blog
TaoSecurity Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
C
Check Point Blog
博客园 - 叶小钗

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
Terraform Drift Detection and Recovery on Google Cloud: Plan, Import, State, and GitHub Actions
Abraham Naiborhu · 2026-05-26 · via DEV Community

Hi! so this is my fourth terraform artefact. I'd like to say that creating infrastructure with Terraform is one thing, but using Terraform when reality changes outside the code is another thing.

For the fourth artifact in my Terraform x Google Cloud portfolio, I wanted to explore a more operational topic:

Terraform drift detection, import, and state recovery.

In this project, i will not be building a large infrastructure platform. This project is about understanding what happens when:

someone changes infrastructure manually
Terraform state no longer matches real infrastructure
an existing resource needs to be imported
a resource is accidentally removed from state
drift needs to be detected automatically

The artifact repository:

terraform-gcp-drift-import-recovery

The main goal:

Understand Terraform failure modes, not only Terraform provisioning.

Why I Built This Artifact

My previous Terraform artifacts focused on building and delivering infrastructure.

The portfolio journey looked like this:

Artifact 1 — Terraform GCP Foundation
Artifact 2 — Production-Lite GCP Web Platform
Artifact 3 — Terraform CI/CD with GitHub Actions and WIF
Artifact 4 — Drift Detection, Import, and State Recovery

The first three artifacts answered:

Can I create infrastructure?
Can I structure it properly?
Can I expose it securely?
Can I automate Terraform plan and apply?

This fourth artifact answers a different question:

What happens when Terraform is no longer the only actor changing infrastructure?

That question matters because real cloud environments are rarely perfect.

Someone may change a firewall rule manually.

Someone may create a resource directly from the console.

Someone may remove a resource from Terraform state by mistake.

Someone may perform an emergency change and forget to update the code.

This artifact is about detecting, understanding, and recovering from those situations.

What This Project Demonstrates

This project demonstrates:

Terraform remote state
baseline GCP infrastructure creation
manual drift simulation
terraform plan drift detection
terraform plan -detailed-exitcode
terraform plan -refresh-only
terraform state list
terraform state show
terraform state pull
terraform state rm
terraform import
importing manually created GCP resources
recovering after state removal
scheduled drift detection with GitHub Actions
GitHub issue creation when drift is detected
Workload Identity Federation
no service account JSON key

The lab intentionally uses simple Google Cloud resources:

VPC
subnet
firewall rule
service account

That is deliberate.

The objective is not infrastructure complexity.

The objective is Terraform behavior.

Why I Created a Separate Repository

I created this as a separate repository instead of adding it into my production-lite web platform repo.

Reason:

This lab intentionally creates abnormal infrastructure situations.

It includes:

manual changes outside Terraform
state removal
resource import
drift simulation
drift detection
recovery scenarios

Those activities should not be mixed into a main application platform repository.

This project is a controlled failure-mode lab.

Repository Structure

The repository is structured like this:

terraform-gcp-drift-import-recovery/
├── README.md
├── .gitignore
├── versions.tf
├── providers.tf
├── backend.tf.example
├── main.tf
├── variables.tf
├── outputs.tf
├── terraform.tfvars.example
├── locals.tf
├── environments/
│   └── dev.tfvars
├── imports/
│   ├── imported-network.tf.example
│   └── imported-firewall.tf.example
├── docs/
│   ├── architecture.md
│   ├── drift-detection-lab.md
│   ├── import-lab.md
│   ├── state-recovery-lab.md
│   ├── scheduled-drift-detection.md
│   ├── operations-runbook.md
│   ├── verification.md
│   ├── design-decisions.md
│   └── release-process.md
├── scripts/
│   ├── simulate-drift.sh
│   ├── create-manual-import-resources.sh
│   ├── cleanup-manual-import-resources.sh
│   └── bootstrap-wif-github.sh
└── .github/
    ├── workflows/
    │   ├── terraform-plan.yml
    │   └── drift-detection.yml
    └── ISSUE_TEMPLATE/
        └── drift_report.md

The structure separates:

baseline Terraform code
manual drift scripts
import examples
state recovery documentation
scheduled drift detection workflow
GitHub issue template

Baseline Infrastructure

The baseline infrastructure is intentionally small.

It creates:

custom VPC
subnet
firewall rule
service account

The firewall rule is the main drift target.

Why?

Because firewall rules are easy to modify manually and easy for Terraform to detect.

For example, Terraform may define:

allowed port: tcp:80

Then someone manually changes it to:

allowed ports: tcp:80,tcp:8080

That difference is drift.

Remote State Design

This project uses a GCS backend for Terraform state.

I added two GitHub repository variables for the state configuration:

TF_STATE_BUCKET = terraform-gcp-production-lite-tfstate
TF_STATE_PREFIX = terraform-gcp-drift-import-recovery/dev

Instead of committing a real backend.tf, the GitHub Actions workflow writes the backend config dynamically.

The workflow step looks like this:

- name: Write Terraform backend config
  shell: bash
  env:
    TF_STATE_BUCKET: ${{ vars.TF_STATE_BUCKET }}
    TF_STATE_PREFIX: ${{ vars.TF_STATE_PREFIX }}
  run: |
    set -euo pipefail

    if [[ -z "${TF_STATE_BUCKET}" ]]; then
      echo "TF_STATE_BUCKET repository variable is required." >&2
      exit 1
    fi

    if [[ -z "${TF_STATE_PREFIX}" ]]; then
      echo "TF_STATE_PREFIX repository variable is required." >&2
      exit 1
    fi

    cat > backend.tf <<EOF
    terraform {
      backend "gcs" {
        bucket = "${TF_STATE_BUCKET}"
        prefix = "${TF_STATE_PREFIX}"
      }
    }
    EOF

This keeps the backend configuration flexible across environments.

It also avoids hardcoding my state bucket and prefix directly in the workflow logic.

Lab 1 — Simulating Drift

The first lab simulates drift manually.

After deploying the baseline infrastructure, I modify the firewall rule outside Terraform.

Example:

export FIREWALL_RULE_NAME="$(terraform output -raw firewall_rule_name)"

gcloud compute firewall-rules update "${FIREWALL_RULE_NAME}" \
  --allow=tcp:80,tcp:8080

At this point:

Terraform code says: tcp:80
Google Cloud says: tcp:80,tcp:8080

That is drift.

Terraform code, Terraform state, and real infrastructure are no longer aligned.

Lab 2 — Detecting Drift with Terraform Plan

After simulating drift, I run:

terraform plan -var-file=environments/dev.tfvars

Terraform detects that the real firewall rule no longer matches the desired configuration.

The plan proposes to restore the firewall rule back to the Terraform-defined state.

That is the simplest drift detection mechanism:

Run terraform plan.
If Terraform proposes unexpected changes, investigate.

Lab 3 — Using -detailed-exitcode

For automation, I used:

terraform plan \
  -input=false \
  -no-color \
  -detailed-exitcode \
  -var-file=environments/dev.tfvars \
  -out=tfplan

The important behavior:

exit code 0 = no changes
exit code 1 = error
exit code 2 = changes detected

This is useful because a CI/CD workflow can use the exit code to determine whether drift exists.

In this artifact:

0 means no drift
2 means drift detected
1 means Terraform error

Lab 4 — Refresh-Only Planning

I also documented refresh-only planning:

terraform plan \
  -var-file=environments/dev.tfvars \
  -refresh-only

This is useful when I want to inspect how real infrastructure differs from Terraform state.

Normal plan answers:

What would Terraform change to make infrastructure match the code?

Refresh-only plan answers:

What has changed in real infrastructure compared to Terraform state?

Both are useful, but they answer different operational questions.

Lab 5 — Recovering from Drift

If the manual change was not approved, the recovery is simple:

terraform apply -var-file=environments/dev.tfvars

Terraform restores the resource to the desired configuration.

In this case, it removes the manually added port 8080.

Then I can confirm that the environment is clean:

terraform plan \
  -var-file=environments/dev.tfvars \
  -detailed-exitcode

Expected result:

exit code 0

That means Terraform sees no pending changes.

Lab 6 — Importing Existing Infrastructure

The next scenario is different.

Instead of changing an existing Terraform-managed resource, I create a resource manually outside Terraform.

Example:

gcloud compute networks create manual-import-network \
  --project="${PROJECT_ID}" \
  --subnet-mode=custom

This VPC exists in Google Cloud, but Terraform does not know about it.

To bring it under Terraform management, I need two things:

Terraform resource configuration
terraform import

Example Terraform configuration:

resource "google_compute_network" "imported_network" {
  name                    = "manual-import-network"
  auto_create_subnetworks = false
  routing_mode            = "REGIONAL"
}

Then import:

terraform import \
  -var-file=environments/dev.tfvars \
  google_compute_network.imported_network \
  "projects/${PROJECT_ID}/global/networks/manual-import-network"

After import:

terraform state list
terraform state show google_compute_network.imported_network
terraform plan -var-file=environments/dev.tfvars

The objective is to reach:

No changes.

That means the Terraform configuration and imported real resource are aligned.

Lab 7 — State Inspection

Terraform state is the mapping between Terraform resource addresses and real infrastructure objects.

Useful commands:

terraform state list

terraform state show google_compute_firewall.allow_http_internal

terraform state pull > state-backups/before-recovery.json

I included state inspection because drift and import are hard to understand without understanding state.

Terraform state answers:

Which real resource is this Terraform resource address connected to?

Lab 8 — State Recovery After Accidental State Removal

This lab simulates a state mistake.

First, I back up state:

mkdir -p state-backups

terraform state pull > state-backups/before-state-rm.json

Then I remove a resource from state:

terraform state rm google_compute_firewall.allow_http_internal

Important:

This does not delete the real firewall rule.
It only removes the mapping from Terraform state.

Now Terraform no longer knows that the real firewall rule belongs to the configuration.

If I run:

terraform plan -var-file=environments/dev.tfvars

Terraform may try to create the firewall rule again.

But the resource already exists in Google Cloud.

The correct recovery is to import it back:

terraform import \
  -var-file=environments/dev.tfvars \
  google_compute_firewall.allow_http_internal \
  "projects/${PROJECT_ID}/global/firewalls/${FIREWALL_RULE_NAME}"

Then verify:

terraform state list
terraform plan -var-file=environments/dev.tfvars

The target result:

No changes.

Lab 9 — Scheduled Drift Detection with GitHub Actions

The final part of this artifact is scheduled drift detection.

The workflow runs:

workflow_dispatch
schedule: every day at 23:00 UTC

The workflow performs:

checkout repository
set up Terraform
authenticate to Google Cloud using WIF
write backend.tf from repository variables
terraform init
terraform validate
terraform plan -detailed-exitcode
upload drift plan artifact
create GitHub issue if drift is detected

The key step:

terraform plan \
  -input=false \
  -no-color \
  -detailed-exitcode \
  -var-file=environments/dev.tfvars \
  -out=tfplan > drift-plan.txt

Then the workflow evaluates the exit code:

if [[ "${EXIT_CODE}" -eq 0 ]]; then
  echo "No drift detected."
  exit 0
elif [[ "${EXIT_CODE}" -eq 2 ]]; then
  echo "Drift detected."
  exit 0
else
  echo "Terraform plan failed."
  exit 1
fi

This is intentional.

If drift is detected, the workflow does not fail as a broken pipeline.

It creates a GitHub issue.

Why?

Because drift is not always a technical failure.

Sometimes drift is an intentional emergency change that needs to be reconciled.

The workflow should create visibility, not immediately destroy or revert something.

GitHub Issue Creation When Drift Is Detected

When Terraform returns exit code 2, the workflow creates a GitHub issue:

gh issue create \
  --title "${ISSUE_TITLE}" \
  --body-file drift-issue-body.md \
  --label "drift,terraform,incident"

The issue tells the operator to:

download the drift plan artifact
review whether the drift was intentional or accidental
revert accidental drift with Terraform apply
update Terraform code if the change was intentional
close the issue only after plan returns exit code 0

This turns drift detection into an operational workflow.

Not just a command.

Authentication with Workload Identity Federation

Like my previous Terraform CI/CD artifact, this project does not use a service account JSON key.

GitHub Actions authenticates to Google Cloud using Workload Identity Federation.

The flow is:

GitHub Actions OIDC token
  -> Google Workload Identity Provider
  -> Terraform drift CI/CD service account
  -> Google Cloud APIs

This keeps the workflow keyless.

No long-lived Google Cloud service account key is stored in GitHub.

Why I Do Not Auto-Apply Drift Fixes

It may be tempting to do this:

detect drift
automatically run terraform apply

I intentionally did not do that.

Reason:

Not all drift is accidental.

Some drift may come from:

emergency production fix
approved console change
another migration process
temporary incident workaround

The right first response is:

detect
notify
review
decide
then reconcile

Automatic remediation can be dangerous if the workflow does not understand why the drift happened.

So this artifact creates a GitHub issue instead.

What This Artifact Taught Me

The biggest lesson:

Terraform is not only a provisioning tool.
Terraform is also a control system.

What I Would Do Differently in Production

For a production environment, I would improve this further with:

least-privilege custom IAM role
Slack or email notification
deduplication of drift issues
severity classification
separate environments
policy-as-code checks
cost impact review
state backup automation
manual approval before remediation

But for this artifact, the core objective is complete:

simulate drift
detect drift
understand state
recover state
import resources
automate drift visibility