terraform-state-manager
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseTerraform State Manager
Terraform State 管理器
Эксперт по управлению Terraform state файлами, remote backends, state operations и troubleshooting.
Terraform State 文件、远程后端、状态操作和故障排查专家。
Core Principles
核心原则
State File Security
状态文件安全
yaml
state_security_principles:
- principle: "Never commit state to version control"
reason: "State files contain sensitive information including secrets"
- principle: "Use remote backends for team environments"
reason: "Enables collaboration and prevents state corruption"
- principle: "Enable encryption at rest and in transit"
reason: "Protects sensitive data in state files"
- principle: "Implement state locking"
reason: "Prevents concurrent modifications and corruption"
- principle: "Regular backups with retention policy"
reason: "Enables recovery from accidental deletions or corruption"yaml
state_security_principles:
- principle: "Never commit state to version control"
reason: "State files contain sensitive information including secrets"
- principle: "Use remote backends for team environments"
reason: "Enables collaboration and prevents state corruption"
- principle: "Enable encryption at rest and in transit"
reason: "Protects sensitive data in state files"
- principle: "Implement state locking"
reason: "Prevents concurrent modifications and corruption"
- principle: "Regular backups with retention policy"
reason: "Enables recovery from accidental deletions or corruption"Backend Configuration
后端配置
AWS S3 Backend (Recommended)
AWS S3 后端(推荐)
hcl
undefinedhcl
undefinedbackend.tf
backend.tf
terraform {
backend "s3" {
bucket = "mycompany-terraform-state"
key = "environments/prod/infrastructure/terraform.tfstate"
region = "us-east-1"
encrypt = true
kms_key_id = "alias/terraform-state-key"
dynamodb_table = "terraform-state-lock"
# Optional: Assume role for cross-account access
role_arn = "arn:aws:iam::123456789012:role/TerraformStateAccess"
# Optional: Workspace-based key prefix
workspace_key_prefix = "workspaces"}
}
undefinedterraform {
backend "s3" {
bucket = "mycompany-terraform-state"
key = "environments/prod/infrastructure/terraform.tfstate"
region = "us-east-1"
encrypt = true
kms_key_id = "alias/terraform-state-key"
dynamodb_table = "terraform-state-lock"
# Optional: Assume role for cross-account access
role_arn = "arn:aws:iam::123456789012:role/TerraformStateAccess"
# Optional: Workspace-based key prefix
workspace_key_prefix = "workspaces"}
}
undefinedS3 Backend Infrastructure Setup
S3 后端基础设施部署
hcl
undefinedhcl
undefinedstate-backend/main.tf - Run this first with local backend
state-backend/main.tf - Run this first with local backend
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.region
}
terraform {
required_version = ">= 1.5.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.region
}
S3 Bucket for State
S3 Bucket for State
resource "aws_s3_bucket" "terraform_state" {
bucket = "${var.company}-terraform-state-${var.region}"
tags = {
Name = "Terraform State"
Environment = "shared"
ManagedBy = "terraform"
}
lifecycle {
prevent_destroy = true
}
}
resource "aws_s3_bucket_versioning" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = aws_kms_key.terraform_state.arn
}
bucket_key_enabled = true
}
}
resource "aws_s3_bucket_public_access_block" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_bucket_lifecycle_configuration" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
rule {
id = "state-versions"
status = "Enabled"
noncurrent_version_expiration {
noncurrent_days = 90
}
noncurrent_version_transition {
noncurrent_days = 30
storage_class = "STANDARD_IA"
}}
}
resource "aws_s3_bucket" "terraform_state" {
bucket = "${var.company}-terraform-state-${var.region}"
tags = {
Name = "Terraform State"
Environment = "shared"
ManagedBy = "terraform"
}
lifecycle {
prevent_destroy = true
}
}
resource "aws_s3_bucket_versioning" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = aws_kms_key.terraform_state.arn
}
bucket_key_enabled = true
}
}
resource "aws_s3_bucket_public_access_block" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_bucket_lifecycle_configuration" "terraform_state" {
bucket = aws_s3_bucket.terraform_state.id
rule {
id = "state-versions"
status = "Enabled"
noncurrent_version_expiration {
noncurrent_days = 90
}
noncurrent_version_transition {
noncurrent_days = 30
storage_class = "STANDARD_IA"
}}
}
DynamoDB Table for State Locking
DynamoDB Table for State Locking
resource "aws_dynamodb_table" "terraform_lock" {
name = "terraform-state-lock"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
server_side_encryption {
enabled = true
}
point_in_time_recovery {
enabled = true
}
tags = {
Name = "Terraform State Lock"
Environment = "shared"
ManagedBy = "terraform"
}
}
resource "aws_dynamodb_table" "terraform_lock" {
name = "terraform-state-lock"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
server_side_encryption {
enabled = true
}
point_in_time_recovery {
enabled = true
}
tags = {
Name = "Terraform State Lock"
Environment = "shared"
ManagedBy = "terraform"
}
}
KMS Key for State Encryption
KMS Key for State Encryption
resource "aws_kms_key" "terraform_state" {
description = "KMS key for Terraform state encryption"
deletion_window_in_days = 30
enable_key_rotation = true
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "Enable IAM User Permissions"
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
}
Action = "kms:"
Resource = ""
},
{
Sid = "Allow Terraform Role"
Effect = "Allow"
Principal = {
AWS = var.terraform_role_arn
}
Action = [
"kms:Encrypt",
"kms:Decrypt",
"kms:GenerateDataKey"
]
Resource = "*"
}
]
})
tags = {
Name = "Terraform State Key"
}
}
resource "aws_kms_alias" "terraform_state" {
name = "alias/terraform-state-key"
target_key_id = aws_kms_key.terraform_state.key_id
}
data "aws_caller_identity" "current" {}
resource "aws_kms_key" "terraform_state" {
description = "KMS key for Terraform state encryption"
deletion_window_in_days = 30
enable_key_rotation = true
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "Enable IAM User Permissions"
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
}
Action = "kms:"
Resource = ""
},
{
Sid = "Allow Terraform Role"
Effect = "Allow"
Principal = {
AWS = var.terraform_role_arn
}
Action = [
"kms:Encrypt",
"kms:Decrypt",
"kms:GenerateDataKey"
]
Resource = "*"
}
]
})
tags = {
Name = "Terraform State Key"
}
}
resource "aws_kms_alias" "terraform_state" {
name = "alias/terraform-state-key"
target_key_id = aws_kms_key.terraform_state.key_id
}
data "aws_caller_identity" "current" {}
Outputs
Outputs
output "state_bucket_name" {
value = aws_s3_bucket.terraform_state.id
}
output "lock_table_name" {
value = aws_dynamodb_table.terraform_lock.name
}
output "kms_key_arn" {
value = aws_kms_key.terraform_state.arn
}
undefinedoutput "state_bucket_name" {
value = aws_s3_bucket.terraform_state.id
}
output "lock_table_name" {
value = aws_dynamodb_table.terraform_lock.name
}
output "kms_key_arn" {
value = aws_kms_key.terraform_state.arn
}
undefinedAzure Backend
Azure 后端
hcl
terraform {
backend "azurerm" {
resource_group_name = "terraform-state-rg"
storage_account_name = "mycompanytfstate"
container_name = "tfstate"
key = "prod/infrastructure.tfstate"
# Enable encryption
use_azuread_auth = true
}
}hcl
terraform {
backend "azurerm" {
resource_group_name = "terraform-state-rg"
storage_account_name = "mycompanytfstate"
container_name = "tfstate"
key = "prod/infrastructure.tfstate"
# Enable encryption
use_azuread_auth = true
}
}Google Cloud Backend
Google Cloud 后端
hcl
terraform {
backend "gcs" {
bucket = "mycompany-terraform-state"
prefix = "terraform/state"
# Enable encryption
encryption_key = "projects/myproject/locations/global/keyRings/terraform/cryptoKeys/state-key"
}
}hcl
terraform {
backend "gcs" {
bucket = "mycompany-terraform-state"
prefix = "terraform/state"
# Enable encryption
encryption_key = "projects/myproject/locations/global/keyRings/terraform/cryptoKeys/state-key"
}
}State Operations
状态操作
Essential State Commands
核心状态命令
bash
undefinedbash
undefinedView all resources in state
View all resources in state
terraform state list
terraform state list
View specific resource details
View specific resource details
terraform state show aws_instance.web
terraform state show aws_instance.web
View state as JSON
View state as JSON
terraform show -json | jq '.values.root_module.resources'
terraform show -json | jq '.values.root_module.resources'
Pull remote state to local file
Pull remote state to local file
terraform state pull > terraform.tfstate.backup
terraform state pull > terraform.tfstate.backup
Push local state to remote (use with caution!)
Push local state to remote (use with caution!)
terraform state push terraform.tfstate
terraform state push terraform.tfstate
Get outputs from state
Get outputs from state
terraform output -json
undefinedterraform output -json
undefinedResource Refactoring (terraform state mv)
资源重构(terraform state mv)
bash
undefinedbash
undefinedRename a resource
Rename a resource
terraform state mv aws_instance.web aws_instance.app_server
terraform state mv aws_instance.web aws_instance.app_server
Move resource to a module
Move resource to a module
terraform state mv aws_instance.web module.compute.aws_instance.web
terraform state mv aws_instance.web module.compute.aws_instance.web
Move resource from module to root
Move resource from module to root
terraform state mv module.compute.aws_instance.web aws_instance.web
terraform state mv module.compute.aws_instance.web aws_instance.web
Move resource between modules
Move resource between modules
terraform state mv module.old.aws_instance.web module.new.aws_instance.web
terraform state mv module.old.aws_instance.web module.new.aws_instance.web
Move entire module
Move entire module
terraform state mv module.old module.new
terraform state mv module.old module.new
Move resource to different state file
Move resource to different state file
terraform state mv -state-out=other.tfstate aws_instance.web aws_instance.web
undefinedterraform state mv -state-out=other.tfstate aws_instance.web aws_instance.web
undefinedImport Existing Resources
导入现有资源
bash
undefinedbash
undefinedBasic import
Basic import
terraform import aws_instance.web i-1234567890abcdef0
terraform import aws_instance.web i-1234567890abcdef0
Import with provider alias
Import with provider alias
terraform import -provider=aws.west aws_instance.web i-1234567890abcdef0
terraform import -provider=aws.west aws_instance.web i-1234567890abcdef0
Import into module
Import into module
terraform import module.vpc.aws_vpc.main vpc-12345678
terraform import module.vpc.aws_vpc.main vpc-12345678
Import with for_each
Import with for_each
terraform import 'aws_instance.servers["web"]' i-1234567890abcdef0
terraform import 'aws_instance.servers["web"]' i-1234567890abcdef0
Generate import blocks (Terraform 1.5+)
Generate import blocks (Terraform 1.5+)
terraform plan -generate-config-out=generated.tf
undefinedterraform plan -generate-config-out=generated.tf
undefinedImport Block (Terraform 1.5+)
导入块(Terraform 1.5+)
hcl
undefinedhcl
undefinedimports.tf
imports.tf
import {
to = aws_instance.web
id = "i-1234567890abcdef0"
}
import {
to = aws_vpc.main
id = "vpc-12345678"
}
import {
to = module.rds.aws_db_instance.main
id = "mydb-instance"
}
import {
to = aws_instance.web
id = "i-1234567890abcdef0"
}
import {
to = aws_vpc.main
id = "vpc-12345678"
}
import {
to = module.rds.aws_db_instance.main
id = "mydb-instance"
}
With for_each
With for_each
import {
for_each = var.existing_buckets
to = aws_s3_bucket.imported[each.key]
id = each.value
}
undefinedimport {
for_each = var.existing_buckets
to = aws_s3_bucket.imported[each.key]
id = each.value
}
undefinedRemove Resources from State
从状态中移除资源
bash
undefinedbash
undefinedRemove single resource (doesn't destroy actual resource)
Remove single resource (doesn't destroy actual resource)
terraform state rm aws_instance.web
terraform state rm aws_instance.web
Remove resource in module
Remove resource in module
terraform state rm module.compute.aws_instance.web
terraform state rm module.compute.aws_instance.web
Remove entire module
Remove entire module
terraform state rm module.old_module
terraform state rm module.old_module
Remove with for_each
Remove with for_each
terraform state rm 'aws_instance.servers["web"]'
terraform state rm 'aws_instance.servers["web"]'
Dry run - show what would be removed
Dry run - show what would be removed
terraform state rm -dry-run aws_instance.web
undefinedterraform state rm -dry-run aws_instance.web
undefinedReplace/Recreate Resources
替换/重建资源
bash
undefinedbash
undefinedForce replacement of resource (Terraform 0.15.2+)
Force replacement of resource (Terraform 0.15.2+)
terraform apply -replace="aws_instance.web"
terraform apply -replace="aws_instance.web"
Taint resource (legacy, use -replace instead)
Taint resource (legacy, use -replace instead)
terraform taint aws_instance.web
terraform taint aws_instance.web
Untaint resource
Untaint resource
terraform untaint aws_instance.web
---terraform untaint aws_instance.web
---Workspace Management
工作区管理
Workspace Commands
工作区命令
bash
undefinedbash
undefinedList all workspaces
List all workspaces
terraform workspace list
terraform workspace list
Create new workspace
Create new workspace
terraform workspace new staging
terraform workspace new staging
Select workspace
Select workspace
terraform workspace select prod
terraform workspace select prod
Show current workspace
Show current workspace
terraform workspace show
terraform workspace show
Delete workspace (must not be current)
Delete workspace (must not be current)
terraform workspace delete staging
undefinedterraform workspace delete staging
undefinedWorkspace-Aware Configuration
工作区感知配置
hcl
undefinedhcl
undefinedlocals.tf
locals.tf
locals {
environment = terraform.workspace
Environment-specific configurations
config = {
dev = {
instance_type = "t3.small"
min_size = 1
max_size = 2
multi_az = false
}
staging = {
instance_type = "t3.medium"
min_size = 2
max_size = 4
multi_az = true
}
prod = {
instance_type = "t3.large"
min_size = 3
max_size = 10
multi_az = true
}
}
env_config = local.config[local.environment]
}
locals {
environment = terraform.workspace
Environment-specific configurations
config = {
dev = {
instance_type = "t3.small"
min_size = 1
max_size = 2
multi_az = false
}
staging = {
instance_type = "t3.medium"
min_size = 2
max_size = 4
multi_az = true
}
prod = {
instance_type = "t3.large"
min_size = 3
max_size = 10
multi_az = true
}
}
env_config = local.config[local.environment]
}
Use in resources
Use in resources
resource "aws_instance" "app" {
instance_type = local.env_config.instance_type
tags = {
Environment = local.environment
}
}
undefinedresource "aws_instance" "app" {
instance_type = local.env_config.instance_type
tags = {
Environment = local.environment
}
}
undefinedBackend with Workspace Prefix
带工作区前缀的后端
hcl
terraform {
backend "s3" {
bucket = "terraform-state"
key = "app/terraform.tfstate"
region = "us-east-1"
workspace_key_prefix = "workspaces"
dynamodb_table = "terraform-lock"
encrypt = true
}
}hcl
terraform {
backend "s3" {
bucket = "terraform-state"
key = "app/terraform.tfstate"
region = "us-east-1"
workspace_key_prefix = "workspaces"
dynamodb_table = "terraform-lock"
encrypt = true
}
}Results in state paths:
Results in state paths:
- workspaces/dev/app/terraform.tfstate
- workspaces/dev/app/terraform.tfstate
- workspaces/staging/app/terraform.tfstate
- workspaces/staging/app/terraform.tfstate
- workspaces/prod/app/terraform.tfstate
- workspaces/prod/app/terraform.tfstate
---
---State Locking
状态锁定
DynamoDB Lock Table Schema
DynamoDB 锁表架构
hcl
resource "aws_dynamodb_table" "terraform_lock" {
name = "terraform-state-lock"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
# Enable encryption
server_side_encryption {
enabled = true
}
# Enable point-in-time recovery
point_in_time_recovery {
enabled = true
}
tags = {
Name = "Terraform State Lock Table"
}
}hcl
resource "aws_dynamodb_table" "terraform_lock" {
name = "terraform-state-lock"
billing_mode = "PAY_PER_REQUEST"
hash_key = "LockID"
attribute {
name = "LockID"
type = "S"
}
# Enable encryption
server_side_encryption {
enabled = true
}
# Enable point-in-time recovery
point_in_time_recovery {
enabled = true
}
tags = {
Name = "Terraform State Lock Table"
}
}Force Unlock
强制解锁
bash
undefinedbash
undefinedForce unlock (use only when you're sure no operation is running)
Force unlock (use only when you're sure no operation is running)
terraform force-unlock LOCK_ID
terraform force-unlock LOCK_ID
The lock ID is shown in the error message when locked:
The lock ID is shown in the error message when locked:
"Error: Error locking state: Error acquiring the state lock: ConditionalCheckFailedException..."
"Error: Error locking state: Error acquiring the state lock: ConditionalCheckFailedException..."
Lock ID: 12345678-1234-1234-1234-123456789012
Lock ID: 12345678-1234-1234-1234-123456789012
undefinedundefinedLock Troubleshooting
锁定故障排查
bash
undefinedbash
undefinedCheck for existing locks in DynamoDB
Check for existing locks in DynamoDB
aws dynamodb scan
--table-name terraform-state-lock
--projection-expression "LockID, Info"
--output table
--table-name terraform-state-lock
--projection-expression "LockID, Info"
--output table
aws dynamodb scan
--table-name terraform-state-lock
--projection-expression "LockID, Info"
--output table
--table-name terraform-state-lock
--projection-expression "LockID, Info"
--output table
Delete stale lock manually (last resort)
Delete stale lock manually (last resort)
aws dynamodb delete-item
--table-name terraform-state-lock
--key '{"LockID": {"S": "terraform-state/path/to/terraform.tfstate"}}'
--table-name terraform-state-lock
--key '{"LockID": {"S": "terraform-state/path/to/terraform.tfstate"}}'
---aws dynamodb delete-item
--table-name terraform-state-lock
--key '{"LockID": {"S": "terraform-state/path/to/terraform.tfstate"}}'
--table-name terraform-state-lock
--key '{"LockID": {"S": "terraform-state/path/to/terraform.tfstate"}}'
---State Migration
状态迁移
Migrate from Local to Remote Backend
从本地后端迁移到远程后端
bash
undefinedbash
undefinedStep 1: Add backend configuration to your Terraform files
Step 1: Add backend configuration to your Terraform files
backend.tf (see S3 backend example above)
backend.tf (see S3 backend example above)
Step 2: Initialize with migration
Step 2: Initialize with migration
terraform init -migrate-state
terraform init -migrate-state
Terraform will prompt:
Terraform will prompt:
Do you want to copy existing state to the new backend?
Do you want to copy existing state to the new backend?
Enter "yes"
Enter "yes"
Step 3: Verify migration
Step 3: Verify migration
terraform state list
terraform plan # Should show no changes
undefinedterraform state list
terraform plan # Should show no changes
undefinedMigrate Between Remote Backends
在远程后端之间迁移
bash
undefinedbash
undefinedStep 1: Pull current state
Step 1: Pull current state
terraform state pull > terraform.tfstate.backup
terraform state pull > terraform.tfstate.backup
Step 2: Update backend configuration
Step 2: Update backend configuration
Change backend.tf to new backend
Change backend.tf to new backend
Step 3: Reinitialize with migration
Step 3: Reinitialize with migration
terraform init -migrate-state -force-copy
terraform init -migrate-state -force-copy
Step 4: Verify
Step 4: Verify
terraform state list
terraform plan
undefinedterraform state list
terraform plan
undefinedSplit State into Multiple States
将状态拆分为多个状态文件
bash
undefinedbash
undefinedScenario: Split monolithic state into separate states for each environment
Scenario: Split monolithic state into separate states for each environment
Step 1: Create backup
Step 1: Create backup
terraform state pull > full-state.backup.json
terraform state pull > full-state.backup.json
Step 2: Create new state file for specific resources
Step 2: Create new state file for specific resources
terraform state mv -state-out=env/prod/terraform.tfstate
module.prod_vpc module.prod_vpc
module.prod_vpc module.prod_vpc
terraform state mv -state-out=env/prod/terraform.tfstate
module.prod_app module.prod_app
module.prod_app module.prod_app
terraform state mv -state-out=env/prod/terraform.tfstate
module.prod_vpc module.prod_vpc
module.prod_vpc module.prod_vpc
terraform state mv -state-out=env/prod/terraform.tfstate
module.prod_app module.prod_app
module.prod_app module.prod_app
Step 3: Initialize new state directories with appropriate backends
Step 3: Initialize new state directories with appropriate backends
cd env/prod
terraform init
terraform state push terraform.tfstate
---cd env/prod
terraform init
terraform state push terraform.tfstate
---Recovery Procedures
恢复流程
Recover from Corrupted State
从损坏的状态中恢复
bash
undefinedbash
undefinedStep 1: Check S3 bucket versioning for previous versions
Step 1: Check S3 bucket versioning for previous versions
aws s3api list-object-versions
--bucket terraform-state
--prefix "path/to/terraform.tfstate"
--max-keys 10
--bucket terraform-state
--prefix "path/to/terraform.tfstate"
--max-keys 10
aws s3api list-object-versions
--bucket terraform-state
--prefix "path/to/terraform.tfstate"
--max-keys 10
--bucket terraform-state
--prefix "path/to/terraform.tfstate"
--max-keys 10
Step 2: Download previous version
Step 2: Download previous version
aws s3api get-object
--bucket terraform-state
--key "path/to/terraform.tfstate"
--version-id "versionId123"
recovered-state.json
--bucket terraform-state
--key "path/to/terraform.tfstate"
--version-id "versionId123"
recovered-state.json
aws s3api get-object
--bucket terraform-state
--key "path/to/terraform.tfstate"
--version-id "versionId123"
recovered-state.json
--bucket terraform-state
--key "path/to/terraform.tfstate"
--version-id "versionId123"
recovered-state.json
Step 3: Validate the recovered state
Step 3: Validate the recovered state
terraform show -json recovered-state.json | jq '.values'
terraform show -json recovered-state.json | jq '.values'
Step 4: Push recovered state
Step 4: Push recovered state
terraform state push recovered-state.json
undefinedterraform state push recovered-state.json
undefinedRecover from Deleted State
从已删除的状态中恢复
bash
undefinedbash
undefinedOption 1: Recover from S3 versioning
Option 1: Recover from S3 versioning
aws s3api list-object-versions
--bucket terraform-state
--prefix "path/to/terraform.tfstate"
--bucket terraform-state
--prefix "path/to/terraform.tfstate"
aws s3api list-object-versions
--bucket terraform-state
--prefix "path/to/terraform.tfstate"
--bucket terraform-state
--prefix "path/to/terraform.tfstate"
Look for DeleteMarker and recover the version before it
Look for DeleteMarker and recover the version before it
Option 2: Recover from DynamoDB backup (if PITR enabled)
Option 2: Recover from DynamoDB backup (if PITR enabled)
aws dynamodb restore-table-to-point-in-time
--source-table-name terraform-state-lock
--target-table-name terraform-state-lock-recovered
--restore-date-time 2024-01-15T10:00:00Z
--source-table-name terraform-state-lock
--target-table-name terraform-state-lock-recovered
--restore-date-time 2024-01-15T10:00:00Z
aws dynamodb restore-table-to-point-in-time
--source-table-name terraform-state-lock
--target-table-name terraform-state-lock-recovered
--restore-date-time 2024-01-15T10:00:00Z
--source-table-name terraform-state-lock
--target-table-name terraform-state-lock-recovered
--restore-date-time 2024-01-15T10:00:00Z
Option 3: Reimport all resources
Option 3: Reimport all resources
Create import blocks for each resource and run terraform apply
Create import blocks for each resource and run terraform apply
undefinedundefinedRebuild State from Scratch
从头重建状态
hcl
undefinedhcl
undefinedimports.tf - Generate these by examining your infrastructure
imports.tf - Generate these by examining your infrastructure
Use AWS CLI to discover resources
Use AWS CLI to discover resources
aws ec2 describe-instances --query 'Reservations[].Instances[].[InstanceId, Tags]'
aws ec2 describe-instances --query 'Reservations[].Instances[].[InstanceId, Tags]'
import {
to = aws_vpc.main
id = "vpc-12345678"
}
import {
to = aws_subnet.public["a"]
id = "subnet-aaaaaaaa"
}
import {
to = aws_subnet.public["b"]
id = "subnet-bbbbbbbb"
}
import {
to = aws_instance.web
id = "i-1234567890abcdef0"
}
import {
to = aws_vpc.main
id = "vpc-12345678"
}
import {
to = aws_subnet.public["a"]
id = "subnet-aaaaaaaa"
}
import {
to = aws_subnet.public["b"]
id = "subnet-bbbbbbbb"
}
import {
to = aws_instance.web
id = "i-1234567890abcdef0"
}
Then run:
Then run:
terraform plan -generate-config-out=generated_resources.tf
terraform plan -generate-config-out=generated_resources.tf
Review and merge generated_resources.tf into your configuration
Review and merge generated_resources.tf into your configuration
terraform apply
terraform apply
---
---Drift Detection
漂移检测
Detect Configuration Drift
检测配置漂移
bash
undefinedbash
undefinedStandard drift detection
Standard drift detection
terraform plan -detailed-exitcode
terraform plan -detailed-exitcode
Exit codes:
Exit codes:
0 = No changes
0 = No changes
1 = Error
1 = Error
2 = Changes detected (drift)
2 = Changes detected (drift)
Machine-readable drift detection
Machine-readable drift detection
terraform plan -json | jq '.resource_changes[] | select(.change.actions != ["no-op"])'
terraform plan -json | jq '.resource_changes[] | select(.change.actions != ["no-op"])'
Generate drift report
Generate drift report
terraform plan -json > plan.json
jq '[.resource_changes[] | select(.change.actions != ["no-op"]) | {
address: .address,
actions: .change.actions,
before: .change.before,
after: .change.after
}]' plan.json > drift-report.json
undefinedterraform plan -json > plan.json
jq '[.resource_changes[] | select(.change.actions != ["no-op"]) | {
address: .address,
actions: .change.actions,
before: .change.before,
after: .change.after
}]' plan.json > drift-report.json
undefinedRefresh State (Sync with Reality)
刷新状态(与实际环境同步)
bash
undefinedbash
undefinedRefresh state without applying changes
Refresh state without applying changes
terraform refresh
terraform refresh
Or use plan with refresh-only (Terraform 0.15.4+)
Or use plan with refresh-only (Terraform 0.15.4+)
terraform apply -refresh-only
terraform apply -refresh-only
This updates state to match actual infrastructure
This updates state to match actual infrastructure
without making any changes to infrastructure
without making any changes to infrastructure
undefinedundefinedAutomated Drift Detection Script
自动化漂移检测脚本
bash
#!/bin/bashbash
#!/bin/bashdrift-check.sh
drift-check.sh
set -e
WORKSPACES=("dev" "staging" "prod")
DRIFT_FOUND=false
for ws in "${WORKSPACES[@]}"; do
echo "Checking drift in workspace: $ws"
terraform workspace select "$ws"
# Run plan with detailed exit code
set +e
terraform plan -detailed-exitcode -out=plan-$ws.tfplan > /dev/null 2>&1
EXIT_CODE=$?
set -e
if [ $EXIT_CODE -eq 2 ]; then
echo "⚠️ DRIFT DETECTED in $ws"
terraform show -json plan-$ws.tfplan | jq '.resource_changes[] | select(.change.actions != ["no-op"]) | .address'
DRIFT_FOUND=true
elif [ $EXIT_CODE -eq 0 ]; then
echo "✅ No drift in $ws"
else
echo "❌ Error checking $ws"
fi
rm -f plan-$ws.tfplandone
if [ "$DRIFT_FOUND" = true ]; then
exit 2
fi
---set -e
WORKSPACES=("dev" "staging" "prod")
DRIFT_FOUND=false
for ws in "${WORKSPACES[@]}"; do
echo "Checking drift in workspace: $ws"
terraform workspace select "$ws"
# Run plan with detailed exit code
set +e
terraform plan -detailed-exitcode -out=plan-$ws.tfplan > /dev/null 2>&1
EXIT_CODE=$?
set -e
if [ $EXIT_CODE -eq 2 ]; then
echo "⚠️ DRIFT DETECTED in $ws"
terraform show -json plan-$ws.tfplan | jq '.resource_changes[] | select(.change.actions != ["no-op"]) | .address'
DRIFT_FOUND=true
elif [ $EXIT_CODE -eq 0 ]; then
echo "✅ No drift in $ws"
else
echo "❌ Error checking $ws"
fi
rm -f plan-$ws.tfplandone
if [ "$DRIFT_FOUND" = true ]; then
exit 2
fi
---Security Best Practices
安全最佳实践
IAM Policy for State Access
状态访问的 IAM 策略
json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "TerraformStateAccess",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::terraform-state",
"arn:aws:s3:::terraform-state/*"
],
"Condition": {
"StringEquals": {
"aws:PrincipalTag/Team": "${aws:ResourceTag/Team}"
}
}
},
{
"Sid": "TerraformStateLock",
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:DeleteItem"
],
"Resource": "arn:aws:dynamodb:*:*:table/terraform-state-lock"
},
{
"Sid": "TerraformStateEncryption",
"Effect": "Allow",
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:GenerateDataKey"
],
"Resource": "arn:aws:kms:*:*:key/terraform-state-key-id"
}
]
}json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "TerraformStateAccess",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::terraform-state",
"arn:aws:s3:::terraform-state/*"
],
"Condition": {
"StringEquals": {
"aws:PrincipalTag/Team": "${aws:ResourceTag/Team}"
}
}
},
{
"Sid": "TerraformStateLock",
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:DeleteItem"
],
"Resource": "arn:aws:dynamodb:*:*:table/terraform-state-lock"
},
{
"Sid": "TerraformStateEncryption",
"Effect": "Allow",
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:GenerateDataKey"
],
"Resource": "arn:aws:kms:*:*:key/terraform-state-key-id"
}
]
}State File Inspection
状态文件检查
bash
undefinedbash
undefinedCheck for sensitive data in state
Check for sensitive data in state
terraform state pull | jq '.resources[].instances[].attributes |
to_entries[] |
select(.key | test("password|secret|key|token"; "i")) |
{resource: input.address, sensitive_field: .key}'
terraform state pull | jq '.resources[].instances[].attributes |
to_entries[] |
select(.key | test("password|secret|key|token"; "i")) |
{resource: input.address, sensitive_field: .key}'
List all sensitive values
List all sensitive values
terraform state pull | jq '[
.resources[] |
.instances[] |
.attributes |
to_entries[] |
select(.value != null and (.key | test("password|secret|key|token|credential"; "i")))
] | length'
---terraform state pull | jq '[
.resources[] |
.instances[] |
.attributes |
to_entries[] |
select(.value != null and (.key | test("password|secret|key|token|credential"; "i")))
] | length'
---CI/CD Integration
CI/CD 集成
GitHub Actions State Management
GitHub Actions 状态管理
yaml
undefinedyaml
undefined.github/workflows/terraform.yml
.github/workflows/terraform.yml
name: Terraform
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
TF_VAR_environment: ${{ github.ref == 'refs/heads/main' && 'prod' || 'staging' }}
jobs:
terraform:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
aws-region: us-east-1
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.6.0
- name: Terraform Init
run: terraform init -backend-config="key=envs/${{ env.TF_VAR_environment }}/terraform.tfstate"
- name: Terraform Plan
id: plan
run: terraform plan -no-color -out=tfplan
continue-on-error: true
- name: Comment PR with Plan
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const output = `#### Terraform Plan 📖
\`\`\`
${{ steps.plan.outputs.stdout }}
\`\`\`
`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
});
- name: Terraform Apply
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: terraform apply -auto-approve tfplan
---name: Terraform
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
TF_VAR_environment: ${{ github.ref == 'refs/heads/main' && 'prod' || 'staging' }}
jobs:
terraform:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
aws-region: us-east-1
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.6.0
- name: Terraform Init
run: terraform init -backend-config="key=envs/${{ env.TF_VAR_environment }}/terraform.tfstate"
- name: Terraform Plan
id: plan
run: terraform plan -no-color -out=tfplan
continue-on-error: true
- name: Comment PR with Plan
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const output = `#### Terraform Plan 📖
\`\`\`
${{ steps.plan.outputs.stdout }}
\`\`\`
`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
});
- name: Terraform Apply
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: terraform apply -auto-approve tfplan
---Лучшие практики
最佳实践
- Всегда используй remote backend — local state только для экспериментов
- Включай versioning для state bucket — возможность recovery
- Используй state locking — предотвращает коррупцию state
- Шифруй state at rest и in transit — содержит sensitive data
- Регулярно тестируй recovery procedures — до того как понадобится
- Автоматизируй drift detection — в CI/CD pipeline
- Разделяй state по окружениям — workspaces или отдельные backends
- Ограничивай доступ к state — IAM policies с минимальными правами
- 始终使用远程后端 — 本地状态仅用于实验
- 为状态存储桶启用版本控制 — 支持恢复操作
- 使用状态锁定 — 防止状态损坏
- 加密静态和传输中的状态 — 状态文件包含敏感数据
- 定期测试恢复流程 — 在需要之前做好准备
- 自动化漂移检测 — 集成到 CI/CD 流水线中
- 按环境拆分状态 — 使用工作区或独立后端
- 限制对状态的访问 — 使用最小权限原则的 IAM 策略