1 — Execution Model, Providers & Authentication¶
Code: 01-basics/7-version-constraints/ · 02-gcp-terraform/z-core-gcp-resources/main.tf · Quiz: Providers & Auth
Architect's Points to Keep in Mind¶
1. The Three-Way Reconciliation Model¶
Every terraform plan compares three sources of truth:
| Source | Role |
|---|---|
HCL (.tf files) |
Desired end state |
State (terraform.tfstate) |
What Terraform believes it owns from the last run |
| Live Cloud API | Actual reality fetched during the refresh phase |
- HCL always wins. Manual console edits create drift that the next
applyreverts to match HCL. - Core vs. Provider split:
- Terraform Core (the CLI binary) knows HCL syntax, the dependency graph (DAG), state management, and meta-arguments (
count,for_each,depends_on,lifecycle,provider). It knows nothing about cloud APIs. - Provider Plugins (e.g.,
hashicorp/google) own resource schemas, API calls, and whether a field change is an in-placePATCHor a destructiveForceNewreplacement.
- Terraform Core (the CLI binary) knows HCL syntax, the dependency graph (DAG), state management, and meta-arguments (
2. required_providers vs. provider {} (Interview Classic)¶
They look similar, live in different blocks, and run at different lifecycle stages:
| Block | Purpose | Evaluated During | Required? |
|---|---|---|---|
required_providers (in terraform {}) |
Declares plugin source (hashicorp/google) and version constraint |
terraform init |
Always for every provider used |
provider "<name>" {} |
Configures runtime settings (project, region, alias, credentials) | plan / apply |
Optional if provider needs no config (local, random) or resources set their own args |
- Provider-block
project/region/zoneare fallback defaults only. If every resource in a module explicitly setsproject = var.project_id, theprojectargument inprovider "google" {}is never read. Removing it is a no-op. - Unused providers in
required_providers(e.g., declaringhashicorp/randomwhen norandom_*resource exists) still force plugin downloads on everyinit— clean them up.
3. terraform init, Version Constraints & .terraform.lock.hcl¶
initnever touchesterraform.tfstate. It only downloads provider binaries into.terraform/, initializes the backend, and writes.terraform.lock.hcl. It is idempotent and safe to run at any time.- Constraint vs. Lock:
- Constraint (
version = "~> 8.0"): range of acceptable versions written by you. - Lock file (
.terraform.lock.hcl): exact version and cryptographic checksums chosen byinit.
- Constraint (
- Commit
.terraform.lock.hclto Git; gitignore.terraform/. Binaries in.terraform/are OS/architecture-specific, so teammates and CI runners still runterraform initto download their platform's binary pinned to the lock file's exact version. - Pessimistic constraint operator (
~>):only the last digit specified is allowed to increment:~> 2.4.1means>= 2.4.1, < 2.5.0(patch updates only).~> 2.4means>= 2.4.0, < 3.0.0(minor updates allowed).
terraform initvsterraform init -upgrade: plaininitstrictly honors.terraform.lock.hcl;-upgradere-resolves the newest version allowed by the constraint and updates the lock file.- Pinning the CLI:
required_version = ">= 1.7, < 2.0"insideterraform {}prevents syntax errors when a config uses newer language features (moved,importblocks,optional()).
4. GCP Authentication Precedence¶
Credentials should be ambient — never hardcode keys in HCL. The google provider evaluates credentials in this order (highest priority first):
credentialsfield insideprovider "google" {}(avoid in production).GOOGLE_CREDENTIALS/GOOGLE_APPLICATION_CREDENTIALSenvironment variables (CI runners / WIF credential config files).- Application Default Credentials (ADC):
~/.config/gcloud/application_default_credentials.json, created viagcloud auth application-default login. Stores a long-lived OAuth refresh token; client libraries automatically exchange it for 1-hour access tokens. - Compute/GKE Metadata Server: attached Service Account when running inside GCP (GCE, GKE Workload Identity, Cloud Build).
5. Provider Aliasing (Multi-Region / Multi-Project)¶
- The
providerblock without analiasis the default provider. - Add
alias = "us_east"on a secondprovider "google"block and route resources viaprovider = google.us_east(reference identifier, no quotes). - Module rule: Child modules inherit default providers automatically, but never inherit aliased providers implicitly. Pass them explicitly via
providers = { google = google.us_east }on themoduleblock. Child modules must never declare their ownprovider {}configuration blocks.
Code Reference¶
terraform {
required_providers {
local = {
source = "hashicorp/local"
version = "~> 2.4.1" # >= 2.4.1, < 2.5.0 (patch only)
}
}
}
# Optional for `local` — takes no configuration options
provider "local" {}
resource "local_file" "pet" {
filename = "/tmp/hello.txt"
content = "Pinned provider test"
file_permission = "0700"
}
terraform {
required_version = ">= 1.7, < 2.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 8.0" # >= 8.0, < 9.0
}
}
}
# Default provider (unaliased)
provider "google" {
project = var.project_id
region = "us-central1"
}
# Secondary aliased provider for multi-region resources
provider "google" {
alias = "us_east"
project = var.project_id
region = "us-east1"
}
resource "google_storage_bucket" "east_region_demo" {
provider = google.us_east # unquoted reference
name = "acme-east-${var.project_id}"
location = "US-EAST1"
uniform_bucket_level_access = true
}
module "east_service" {
source = "./modules/service"
providers = { google = google.us_east } # explicit pass required for modules
}
# Authenticate local ADC (writes OAuth refresh token)
gcloud auth application-default login
gcloud config set project YOUR_PROJECT_ID
# Initialize & inspect locked versions
terraform init
cat .terraform.lock.hcl
# Upgrade providers within version constraints & review diff
terraform init -upgrade
git diff .terraform.lock.hcl