Most Terraform tutorials cover the basics — write some HCL, run terraform apply, done. But real day-to-day work with Terraform is about the lifecycle: how you initialize a project, manage workspaces across environments, keep state healthy, and wire it all into a CI/CD pipeline that doesn't break on Friday afternoon.
The core commands
The Terraform lifecycle maps to five main commands you'll run constantly:
terraform init— downloads providers, initializes the backend, sets up modulesterraform validate— syntax and configuration check without hitting any APIsterraform plan— shows a diff of what will change, the most important stepterraform apply— executes the planterraform destroy— tears everything down (use with care in non-dev environments)
The habit that matters most: always review the plan output before applying. Every plan. Even small ones. The number of times a "one-line change" showed an unexpected 15-resource replacement is not zero.
Backend configuration
For anything beyond local testing, state goes in S3 with DynamoDB locking:
terraform {
backend "s3" {
bucket = "my-tfstate-bucket"
key = "infra/prod/terraform.tfstate"
region = "us-east-1"
dynamodb_table = "terraform-state-lock"
encrypt = true
}
}
The DynamoDB table prevents concurrent applies from corrupting state. The bucket should have versioning enabled so you can recover from bad state writes.
Workspace management
Workspaces are the cleanest way to manage multiple environments (dev, staging, prod) from a single configuration. Each workspace gets its own state file under the same backend key prefix.
# Create and switch to a new workspace
terraform workspace new staging
terraform workspace select staging
terraform workspace list
I use a locals block to map workspace names to environment-specific values:
locals {
env = terraform.workspace
config = {
dev = { instance_type = "t3.small", min_size = 1 }
staging = { instance_type = "t3.medium", min_size = 2 }
prod = { instance_type = "m5.large", min_size = 4 }
}
instance_type = local.config[local.env].instance_type
min_size = local.config[local.env].min_size
}
State file hygiene
State drift is the silent killer of Terraform projects. A few rules I follow:
- Never edit state files manually. Use
terraform state mvorterraform importfor surgical changes. - Run
terraform refreshperiodically to sync state with actual infrastructure, especially after any manual console changes. - Lock down the S3 state bucket with bucket policies — only CI/CD roles and senior engineers should be able to write to it.
- Separate state per environment and per major subsystem (networking state, app state, data state) — don't put everything in one monolithic state file.
Integrating with Jenkins CI/CD
A standard Jenkins pipeline for Terraform runs four stages: init, validate, plan, and a manual-approval-gated apply:
pipeline {
agent any
environment {
AWS_DEFAULT_REGION = 'us-east-1'
TF_WORKSPACE = "${params.ENVIRONMENT}"
}
stages {
stage('Init') {
steps { sh 'terraform init -input=false' }
}
stage('Validate') {
steps { sh 'terraform validate' }
}
stage('Plan') {
steps {
sh 'terraform plan -out=tfplan -input=false'
sh 'terraform show -no-color tfplan > plan.txt'
archiveArtifacts 'plan.txt'
}
}
stage('Apply') {
when { branch 'main' }
input { message "Review plan.txt — apply to ${params.ENVIRONMENT}?" }
steps { sh 'terraform apply -input=false tfplan' }
}
}
}
The manual approval gate on apply is non-negotiable for production. Save the plan output as a Jenkins artifact so the reviewer can read exactly what will change before approving.
Lifecycle meta-arguments
Terraform's lifecycle block lets you control how specific resources behave during plan/apply:
resource "aws_instance" "app" {
# ...
lifecycle {
create_before_destroy = true # zero-downtime replacement
prevent_destroy = true # block accidental deletion
ignore_changes = [tags] # don't drift-detect tag changes
}
}
prevent_destroy is especially useful on databases and stateful resources where an accidental destroy would be catastrophic.
Wrapping up
The Terraform lifecycle is straightforward once the patterns click — consistent workspace discipline, healthy state management, and a CI/CD pipeline that enforces review before apply. Get those three right and Terraform becomes a reliable, low-drama part of your infrastructure workflow.