如果你已经在AWS上使用Terraform有一段时间了,那么你应该熟悉这样的配置方案:使用S3存储状态数据,利用DynamoDB进行状态锁定,同时还需要一些IAM策略来将这些组件连接起来。这种方案确实有效,而且多年来一直运行得很好。

然而,这种配置方式其实隐藏着一些很少被公开讨论的成本。这些成本并不仅仅是金钱上的支出——对于多个团队和不同的环境来说,使用按需计费的DynamoDB所带来的费用也会逐渐累积起来。

真正的成本在于复杂性。在Terraform能够开始管理其他事务之前,每一个新的AWS环境都需要先配置相应的资源。每一位初次使用Terraform的工程师都必须理解:为什么明明是逻辑上相同的功能——存储和保护状态数据——却需要由两种完全不同的AWS服务来完成。此外,每当出现锁定机制失效的情况时,总需要有人手动从DynamoDB中删除相关记录,才能使系统恢复正常运行。

2024年11月,AWS宣布S3现在已经支持对Terraform的状态文件进行原生对象锁定,这意味着DynamoDB不再被用于状态锁定功能了。Terraform 1.10版本已经加入了这一功能,目前它已经可以全面使用了。

在本教程中,你将学习到以下内容:

  • 什么是S3原生对象锁定机制,以及它是如何工作的

  • 如果你是刚开始使用Terraform,应该如何从零开始配置这种功能

  • 如何安全地将现有的S3 + DynamoDB配置方案迁移到S3原生锁定机制

  • 如何验证锁定机制是否正常工作,以及如何处理一些特殊情况

通过学习这些内容,你将能够拥有一个更加简单、更易于管理的Terraform后端环境,同时也需要管理的AWS资源也会减少一个。

目录

什么是Terraform状态锁定机制?

在了解这种新机制之前,首先需要明白状态锁定究竟是为了解决什么问题。

Terraform会将关于您基础设施的所有信息存储在一个状态文件中——这个文件是一个JSON格式的文档,它将您的配置信息与实际的AWS资源对应起来。当您运行terraform apply命令时,Terraform会读取这个文件,计算当前资源状态与您的配置之间的差异,并进行必要的调整。

问题出现在当两个工程师或两条CI/CD流水线同时尝试执行修改操作时。如果它们都同时读取状态文件、独立地计算需要进行的更改,然后试图将结果写回系统,就会产生竞争条件。第二个操作会覆盖第一个操作所做的修改,从而导致资源状态与实际状况不一致。这是一个非常严重的问题,因为它可能会导致资源被重复创建或意外丢失。

状态锁定机制通过在任何可能修改资源状态的操作开始时创建一个锁来解决这个问题。如果已经存在锁,Terraform会拒绝继续执行该操作,并显示当前谁持有这个锁以及它是何时获得的。任何时候都只有一个操作可以持有这个锁,当该操作完成后,锁才会被释放。

Terraform A开始运行                状态文件 / 锁机制                    Terraform B开始运行
(用户1)                         (使用S3/DynamoDB存储状态文件)  (用户2)

   |                                   |                            |
   |------- 1. 获取锁 ----------------->|                            |
   |                                   |                            |
   |<------ 2. 锁被授予 ---------------|                            |
   |                                   |                            |
   |------- 3. 再次尝试获取锁 --------->|                            |
   |            [正在处理中]                   |                            |
   |      (修改基础设施)                 |<------ 4. 锁请求被拒绝 -------|
   |                                   |        (等待/重试)                    |
   |                                   |                            |
   |------- 5. 释放锁 ----------------->|                            |
   |                                   |                            |
   |           [操作已完成]                   |<------ 6. 锁再次被授予 --------|
   |                                   |                            |
   |                                   |       [正在处理中]                   |
   |                                   | (修改基础设施)                 |              
   |                                   |                            |

什么是S3原生的状态锁定机制?

之前,Terraform的S3后端使用DynamoDB表作为锁定机制。当需要创建锁时,Terraform会向DynamoDB中写入一条记录,其中LockID字段作为主键。DynamoDB的条件写操作能够确保只有某个进程才能创建这条记录,因此这种锁定机制是原子性的。

S3的原生锁定功能实际上使用的是S3对象锁。S3对象锁是S3提供的一项功能,最初设计目的是为了满足法规要求,确保数据遵循“一次写入、多次读取”的规则。AWS后来扩展了这一功能,使其能够支持Terraform的状态锁定机制。

当在您的Terraform后端启用S3原生锁定功能时:

  1. Terraform会将状态信息写入S3中的.tfstate文件中(与之前相同)。

  2. 为了获取锁,Terraform会使用S3的条件写操作——具体来说是if-none-match条件头,从而原子性地创建锁定文件。

  3. 如果锁定文件已经存在,S3会拒绝写入请求,此时Terraform会报告“锁已被占用”的情况。

  4. 操作完成后,Terraform会删除锁定文件,从而释放锁。

与DynamoDB相比,最大的区别在于:整个锁定机制完全依赖于S3本身,不需要任何额外的服务、IAM权限或资源配置。

注意:此功能要求使用Terraform 1.10.0或更高版本,并且所使用的S3存储桶必须启用了对象锁功能。对象锁必须在创建存储桶时就进行配置;无法通过控制台或CLI为已存在的存储桶启用此功能。不过对于现有存储桶,也有可行的解决方法,我们会在第二部分中详细介绍。

S3原生锁定机制与S3 + DynamoDB组合方案的对比

方面 S3 + DynamoDB(旧方案) S3原生锁定(新方案)
所需AWS服务 S3 + DynamoDB 仅S3
所需的IAM权限 S3 + DynamoDB的权限设置 仅S3的权限即可
Terraform版本要求 任意版本均可 1.10.0或更高版本
配置复杂性 需要两种资源及相应的IAM权限设置 仅需一种资源
锁冲突解决方式 删除DynamoDB中的记录 删除S3中的锁定文件
成本 S3存储费用 + DynamoDB按需使用费用 仅S3存储费用
对象锁的必要性 非必需 在S3存储桶上必须启用
锁定实现机制 DynamoDB的条件写操作 S3的条件写操作(使用if-none-match头)
状态版本控制 S3版本控制功能(推荐使用) S3版本控制功能(为确保数据安全必须启用)

从Terraform的角度来看,这两种方案的功能表现是完全相同的。锁的生成方式、锁定信息的表现形式都是一样的,唯一的不同在于底层实现的细节。

先决条件

在开始之前,请确保您已经具备以下条件:

  • 已安装Terraform 1.10.0或更高版本。请检查您的版本:
terraform version

如果需要升级,请按照官方升级指南进行操作。

  • 已安装AWS CLI,并且配置了具有创建和管理S3桶权限的认证信息。
aws --version
aws sts get-caller-identity   # 确认您已经成功登录
  • 需要具备IAM权限,以便执行以下S3操作:

    • s3:CreateBucket

    • s3:PutBucketVersioning

    • s3:PutBucketEncryption

    • s3:PutObjectLegalHold

    • s3:PutObjectRetention

    • s3:GetObject

    • s3:PutObject

    • s3:DeleteObject

    • s3:ListBucket

  • 对于迁移路径而言,您需要能够访问现有的Terraform项目以及当前正在使用的S3桶和DynamoDB表。

第1部分:全新设置——从零开始配置S3原生锁定功能

如果您正在创建一个新的Terraform项目,并且希望从一开始就使用S3原生锁定功能,请阅读本节内容。

步骤1:创建具有版本控制和加密功能的S3桶

对象锁定功能必须在创建桶时启用。您无法通过标准控制台流程在之后添加这一功能。请使用AWS CLI创建启用了对象锁定的桶:

aws s3api create-bucket \
  --bucket your-project-terraform-state \
  --region us-east-1 \
  --object-lock-enabled-for-bucket

注意:对于us-east-1以外的地区,需要添加--create-bucket-configuration参数。

aws s3api create-bucket \
  --bucket your-project-terraform-state \
  --region eu-west-1 \
  --create-bucket-configuration LocationConstraint=eu-west-1 \
  --object-lock-enabled-for-bucket

接下来为该桶启用版本控制功能。版本控制与对象锁定功能一起使用,可以在出现问题时帮助Terraform恢复之前的配置状态。

aws s3api put-bucket-versioning \
  --bucket your-project-terraform-state \
  --versioning-configuration Status=Enabled

还要启用服务器端加密功能,这样您的配置文件在存储时就会被加密:

aws s3api put-bucket-encryption \
  --bucket your-project-terraform-state \
  --server-side-encryption-configuration '{
    "Rules": [
      {
        "ApplyServerSideEncryptionByDefault": {
          "SSEAlgorithm": "AES256"
        },
        "BucketKeyEnabled": true
      }
    ]
  }'

阻止所有公众访问该存储桶。Terraform状态文件中包含资源ID、IP地址以及一些可能敏感的信息,因此这些文件绝对不能被公开访问:

aws s3api put-public-access-block \
  --bucket your-project-terraform-state \
  --public-access-block-configuration \
    "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"

验证存储桶的配置设置:

# 确认对象锁定功能已启用
aws s3api get-object-lock-configuration \
  --bucket your-project-terraform-state
 
# 确认版本控制功能已启用
aws s3api get-bucket-versioning \
  --bucket your-project-terraform-state
 
# 确认加密功能已配置
aws s3api get-bucket-encryption \
  --bucket your-project-terraform-state

对象锁定功能检查的预期输出结果如下:

{
    "ObjectLockConfiguration": {
        "ObjectLockEnabled": "Enabled"
    }
}

终端窗口中显示的AWS CLI命令,用于验证S3存储桶是否正确配置了对象锁定、版本控制及加密功能

步骤2:使用原生锁定功能配置Terraform后端

在您的Terraform项目中,创建或更新`backend.tf`文件:

terraform {
  backend "s3" {
    bucket = "your-project-terraform-state"
    key    = "production/terraform.tfstate"
    region = "us-east-1"
 
    # 启用S3的原生状态锁定功能
    # 需要使用Terraform 1.10.0及以上版本,并且存储桶必须已启用对象锁定功能
    use_lockfile = true
 
    # 数据静默加密
    encrypt = true
  }
}

与旧配置相比,最关键的变化就是添加了`use_lockfile = true`这一参数。请注意:旧的配置中没有`dynamodb_table`这个参数,也就是说不需要使用DynamoDB服务。

以下是旧配置与新配置的对比:

旧配置(S3 + DynamoDB):

terraform {
  backend "s3" {
    bucket         = "your-project-terraform-state"
    key            = "production/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-state-lock"   # 这一行在新的配置中已被删除
  }
}

新配置(使用S3的原生锁定功能):

terraform {
  backend "s3" {
    bucket       = "your-project-terraform-state"
    key          = "production/terraform.tfstate"
    region       = "us-east-1"
    encrypt      = true
    use_lockfile = true   # 这一行替代了原来的dynamodb_table参数
  }
}

步骤 3:初始化并验证配置

运行 terraform init 命令来初始化后端配置:

terraform init

预期的输出结果如下:

正在初始化后端配置…
后端“s3”已成功配置!除非后端配置发生变更,否则Terraform会自动使用该后端。
 
正在初始化插件模块…
Terraform已成功完成初始化!

运行 terraform plan 命令来确认所有配置是否正常工作:

terraform plan

如果锁定机制能够正常发挥作用,在输出结果出现之前,你会看到Terraform在获取锁定权限时会有短暂的延迟。你也可以通过查看S3存储桶来确认锁定状态——在操作过程中,.tflock文件会暂时出现在该存储桶中,操作完成后这些文件就会消失。

第2部分:迁移流程——如何从使用S3 + DynamoDB的方案切换到使用S3原生锁定机制

如果你当前已经使用了S3存储桶和DynamoDB表来进行状态锁定,并且希望将系统迁移到使用S3原生锁定机制的模式,那么请阅读这一部分内容。

重要提示:进行迁移操作需要确保没有其他Terraform命令正在运行。由于你需要修改后端配置,因此所有团队成员以及CI/CD自动化流程在迁移期间都必须停止执行terraform planterraform apply命令》。整个迁移过程耗时不到10分钟。

步骤 1:验证当前的配置状态

在进行任何修改之前,先记录下当前的后端配置信息,并确认状态文件是否可以正常访问:

# 确认状态文件是否存储在S3中
aws s3 ls s3://your-existing-bucket/path/to/terraform.tfstate
 
# 确认DynamoDB表是否存在
aws dynamodb describe-table \
  --table-name your-dynamodb-lock-table \
  --query 'Table.TableStatus'

查看当前的backend.tf文件,记录下其中所有的配置值:

# 请在修改任何配置之前先记下这些值
terraform {
  backend "s3" {
    bucket         = "your-existing-bucket"       # 请记住这个值
    key            = "path/to/terraform.tfstate"   # 也请记住这个值
    region         = "us-east-1"                   # 这个值同样需要记录下来
    encrypt        = true
    dynamodb_table = "your-dynamodb-lock-table"    # 这一行内容在迁移后会被删除
  }
}

最后再运行一次 terraform plan 命令,确认当前的配置状态是否正常,以及是否存在任何未预期的变化:

terraform plan

如果计划执行结果显示没有异常变化,那么你就可以安全地继续进行后续的迁移操作了。

步骤2:为现有的S3桶启用对象锁定功能

这是迁移过程中最为重要的一步。通常情况下,无法为已存在的桶启用对象锁定功能;这一设置必须在创建桶时进行配置。

不过,AWS提供了一种方法,可以通过提交支持请求或通过标准控制台界面未提供的直接API调用,为现有的桶启用对象锁定功能。AWS已经正式将这种操作方式记录在相关文档中,供Terraform迁移场景使用。

运行以下AWS CLI命令,为您的现有桶启用对象锁定功能:

aws s3api put-object-lock-configuration \
  --bucket your-existing-bucket \
  --object-lock-configuration '{"ObjectLockEnabled": "Enabled"}'

注意:此命令会以治理模式启用对象锁定功能,且不会设置默认的保留期限。这意味着它仅会激活锁定机制,而不会为所有对象指定固定的保留时间。这正是Terraform原生锁定功能所需要的:能够创建和删除锁定文件,而不需要永久保存对象数据。

验证对象锁定功能是否已成功启用:

aws s3api get-object-lock-configuration \
  --bucket your-existing-bucket

预期输出结果如下:

{
    "ObjectLockConfiguration": {
        "ObjectLockEnabled": "Enabled"
    }
}

同时,请确认版本控制功能是否已经启用(如果您正在使用生产环境的Terraform环境,这个功能应该是已启用的):

aws s3api get-bucket-versioning \
  --bucket your-existing-bucket

预期输出结果如下:

{
    "Status": "Enabled"
}

如果版本控制功能尚未启用,请在继续下一步操作之前先将其启用:

aws s3api put-bucket-versioning \
  --bucket your-existing-bucket \
  --versioning-configuration Status=Enabled

使用AWS CLI成功为现有S3桶启用对象锁定功能的终端输出界面

步骤3:更新Terraform后端配置文件

请修改您的backend.tf文件,删除dynamodb_table参数,并添加use_lockfile = true这一行:

terraform {
  backend "s3" {
    bucket = "your-existing-bucket"
    key    = "path/to/terraform.tfstate"
    region = "us-east-1"
    encrypt = true
 
    # 添加以下代码:
    use_lockfile = true
 
    # 完全删除以下这一行:
    # dynamodb_table = "your-dynamodb-lock-table"
  }
}

修改后的backend.tf文件应该如下所示:

terraform {
  backend "s3" {
    bucket       = "你的现有S3桶名"
    key          = "path/to/terraform.tfstate"
    region       = "us-east-1"
    encrypt      = true
    use_lockfile = true
  }
}

步骤4:重新初始化Terraform

运行命令terraform init,并加上参数-reconfigure

terraform init -reconfigure

预期的输出结果如下:

正在初始化后端配置…
后端“s3”已成功配置!除非后端配置再次发生变化,否则Terraform会自动使用这个后端。
 
正在初始化插件…
- 从依赖关系锁文件中重新使用先前版本的hashicorp/aws插件
 
Terraform已成功初始化!

如果在这里看到错误:最常见的原因是S3桶上的对象锁定功能没有成功启用。请先重新执行步骤2中的验证操作,然后再继续下一步。

步骤5:验证迁移结果

运行terraform plan命令,以确认Terraform是否能够正确地使用新的后端配置:

terraform plan

规划结果应该满足以下条件:

  • 操作成功完成

  • 显示的结果与步骤1中运行的规划结果相同(没有变化,或者变化内容与之前一致)

  • 输出结果中绝对不能出现与DynamoDB相关的任何信息

为了确认锁定机制确实是在使用S3而不是DynamoDB,可以打开第二个终端,在第一个终端运行terraform plan命令的同时,在第二个终端中也运行这个命令。你会看到第二个终端的输出中会出现与S3锁定相关的语句,而不会提到DynamoDB:

╷
│ 错误:获取状态锁时出现错误
│
│ 错误信息:操作失败,原因是在执行S3的PutObject请求时出现了错误,响应码为409,
│ 请求ID:……,错误原因是该键已经被其他进程锁定。
│
│ 锁定信息:
│   ID:        a1b2c3d4-e5f6-7890-abcd-ef1234567890
│   路径:      your-existing-bucket/path/to/terraform.tfstate.tflock
│   操作类型:OperationTypePlan
│   执行者:    user@hostname
│   版本:     1.10.0
│   创建时间: 2026-05-06 14:22:01 UTC
│   其他信息:
╵

Path字段显示的文件名为.tfstate.tflock,这个文件保存在你的S3桶中,而不是DynamoDB数据库中。这一结果证明了锁定机制现在完全由S3来处理。

两个终端同时运行Terraform规划命令,第二个终端显示的锁定错误信息证实了S3的原生锁定功能正在正常工作

步骤6:清理DynamoDB表

在确认迁移过程正常进行,并且您的团队已经使用新的后端至少完成了一次成功的计划制定应用操作之后,就可以删除DynamoDB表了。

如果你们使用了CI/CD流程,或者有多名团队成员参与这项工作,请至少等待24到48小时再删除DynamoDB表。这样就能有足够的时间确保所有相关流程都已经更新为使用新的后端配置。

准备就绪后,可以执行以下命令来删除DynamoDB表:

aws dynamodb delete-table \
  --table-name your-dynamodb-lock-table

确认表已被成功删除后,可以再次执行以下命令:

aws dynamodb describe-table \
  --table-name your-dynamodb-lock-table

预期的输出结果应该是:

调用DescribeTable操作时出现了错误(ResourceNotFoundException):
请求的资源未找到

如果出现这种错误,说明表确实已经被删除了,迁移工作已经完成。

如果您是通过Terraform来配置DynamoDB表的(这是推荐的做法),请先从Terraform配置中移除该资源,然后运行terraform apply命令,通过Terraform而非CLI直接删除该资源。这样就能保持配置的整洁性:

# 从您的Terraform配置中删除以下代码块:
resource "aws_dynamodb_table" "terraform_state_lock" {
  name         = "terraform-state-lock"
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = "LockID"
 
  attribute {
    name = "LockID"
    type = "S"
  }
}

删除完该代码块后,运行以下命令:

terraform apply

Terraform会检测到DynamoDB表资源已被从配置中移除,然后自动删除该表。

如何验证锁定机制是否正常工作

无论您是完成了新的配置设置,还是完成了迁移工作,都可以使用以下方法来独立验证锁定机制是否正常运作。

方法1:在操作过程中观察锁定文件

在一个终端中,针对包含大量资源的配置文件执行长时间运行的计划制定操作:

terraform plan

在计划执行期间,在另一个终端中检查S3存储桶中是否出现了锁定文件:

aws s3 ls s3://your-bucket/path/to/ | grep tflock

你应该会看到类似这样的文件:

2026-05-06 14:22:01        512 terraform.tfstate.tflock

当计划制定操作完成后,再次运行上述命令,此时.tflock文件应该已经消失了。

方法2:读取锁定文件的内容

当某个计划正在运行时,可以下载并查看该锁文件的内容:

aws s3 cp \
  s3://your-bucket/path/to/terraform.tfstate.tflock \
  /tmp/current.lock && cat /tmp/current.lock

预期的输出结果(为便于阅读而进行了格式化)如下:

{
  "ID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "Operation": "OperationTypePlan",
  "Info": "",
  "Who": "tolani@dev-machine",
  "Version": "1.10.0",
  "Created": "2026-05-06T14:22:01.123456789Z",
  "Path": "your-bucket/path/to/terraform.tfstate"
}

这就是Terraform在检测到锁被占用时所显示的相同信息。现在,这些锁信息是以JSON文件的形式存储在S3中,而不是保存在DynamoDB中。

如何处理无法解除的锁

在使用DynamoDB作为后端时,解决锁冲突意味着需要从DynamoDB表中删除相应的记录;而使用S3的原生锁定机制时,则只需删除S3中的.tflock文件即可。

以下是一些可能导致锁无法解除的情况:

  • terraform applyplan进程在执行过程中被中断时

  • 当CI/CD流水线中的某个步骤在Terraform操作进行期间发生故障时

  • 网络故障导致锁的释放操作无法完成时

以下是检查是否存在无法解除的锁的方法:

aws s3 ls s3://your-bucket/path/to/ | grep tflock

如果存在.tflock文件,且当前没有正在运行的Terraform操作,那么就说明存在无法解除的锁。

你还可以查看该锁文件的内容,从而了解是谁持有这个锁:

aws s3 cp \
  s3://your-bucket/path/to/terraform.tfstate.tflock \
  /tmp/stuck.lock && cat /tmp/stuck.lock

通过这些信息,你可以知道是哪个用户或进程持有这个锁,具体执行了什么操作,以及该锁是在什么时候被获取的。

如果你想强制解除这个锁,可以使用以下命令:

terraform force-unlock LOCK-ID

请将LOCK-ID替换为锁文件中显示的ID值。例如:

terraform force-unlock a1b2c3d4-e5f6-7890-abcd-ef1234567890

Terraform会提示你确认是否真的要强制解除锁:

您确定要强制解除锁吗?
Terraform将会删除远程状态上的锁。
这样,本地的Terraform命令就可以修改这个状态了,即使该状态可能仍在被其他进程使用中。
只有输入“yes”才能确认操作。

输入“yes”后,Terraform会成功解除锁。

另一种方法是通过CLI直接删除.tflock文件。如果terraform force-unlock命令无法使用(例如,因为在没有安装Terraform的CI环境中运行),可以直接删除该文件:

aws s3 rm s3://your-bucket/path/to/terraform.tfstate.tflock

只有当你确定当前没有任何Terraform操作正在运行时,才应该删除锁定文件。如果删除了某个仍被正在运行的操作所持有的锁定文件,那么就会允许另一个并发操作开始执行,而这正是锁定机制旨在防止的情况。

回滚计划:如果出现问题

如果在迁移后遇到了问题,你可以按照以下步骤将系统恢复到S3 + DynamoDB的配置状态。

步骤1:停止你团队以及CI/CD管道中所有的Terraform操作。
步骤2:如果你已经删除了DynamoDB表,请重新创建它:

aws dynamodb create-table \
  --table-name terraform-state-lock \
  --attribute-definitions AttributeName=LockID,AttributeType=S \
  --key-schema AttributeName=LockID,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST

步骤3:将`backend.tf`文件恢复到之前的配置状态。

terraform {
  backend "s3" {
    bucket         = "your-existing-bucket"
    key            = "path/to/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-state-lock"   # 已恢复到之前的配置
    # 可以删除这一行:use_lockfile = true
  }
}

步骤4:重新初始化Terraform环境:

terraform init -reconfigure

步骤5:验证配置是否正确:

terraform plan

在回滚过程中,状态文件本身并不会被修改,因此数据也不会丢失。唯一会发生变化的,就是Terraform会使用哪种锁定机制来保护这些文件。

注意:即使在S3桶上启用了对象锁功能,也不会影响系统的回滚操作。对象锁与DynamoDB的锁定机制可以同时存在;在后端配置中指定`dynamodb_table`,就能确保无论桶是否启用了对象锁,Terraform都会使用DynamoDB来进行锁定。

状态桶的安全最佳实践

将系统迁移至支持原生锁定的S3存储服务时,正是检查状态桶整体安全配置的好时机。以下是所有生产环境中的Terraform状态桶都应该遵循的安全最佳实践:

必须启用版本控制功能

为了确保S3的原生锁定机制能够安全地发挥作用,版本控制功能是必不可少的。它能够保证在状态文件被意外覆盖或损坏的情况下,你可以恢复到之前的版本。

aws s3api put-bucket-versioning \
  --bucket your-state-bucket \
  --versioning-configuration Status=Enabled

必须禁止所有公共访问

你的状态文件中包含了资源ARN、IP地址,以及可能通过Terraform变量传递的敏感信息。因此,这些文件绝对不能被公开访问。

aws s3api put-public-access-block \
  --bucket your-state-bucket \
  --public-access-block-configuration \
    "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"

启用服务器端加密

务必对静态状态文件进行加密。最低要求是使用AES256算法;如果您的组织需要使用KMS进行密钥管理,可以执行以下操作:

aws s3api put-bucket-encryption \
--bucket your-state-bucket \
--server-side-encryption-configuration '{
"Rules": [
{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "aws:kms",
"KMSMasterKeyID": "arn:aws:kms:us-east-1:123456789012:key/your-kms-key-id"
},
"BucketKeyEnabled": true
}
]
}'

应用最小权限IAM策略

Terraform用于访问状态存储桶的角色或用户应仅拥有其所需的权限。以下是针对S3原生加密功能的最小权限配置示例:

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "TerraformStateAccess",
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": [
"arn:aws:s3:::your-state-bucket",
"arn:aws:s3:::your-state-bucket/*"
]
},
{
"Sid": "TerraformStateLocking",
"Effect": "Allow",
"Action": [
"s3:GetObjectLegalHold",
"s3:PutObjectLegalHold",
"s3:GetObjectRetention",
"s3:PutObjectRetention"
],
"Resource": "arn:aws:s3:::your-state-bucket/*.tflock"
}
]
}

请注意:此配置中并未包含访问DynamoDB的权限。这样的权限设置比旧方法更为简洁、所需权限也更少。

启用访问日志记录功能

在CloudTrail或S3服务器访问日志中记录对状态存储桶的所有访问操作。这样就可以追踪每次状态数据被读取、写入或被锁定时的详细情况:

aws s3api put-bucket-logging \
--bucket your-state-bucket \
--bucket-logging-status '{
"LoggingEnabled": {
"TargetBucket": "your-logging-bucket",
"TargetPrefix": "terraform-state-access/"
}
}'

总结

使用AWS S3的原生状态锁定功能,无需在Terraform后端环境中配置DynamoDB表。这样一来,基础设施结构会更加简洁,所需的IAM权限也会减少,在团队管理的所有环境中,都需要配置、监控和付费的服务数量也会相应减少。

以下是您所完成的工作的总结:

  • 了解了什么是状态锁定机制,以及为什么它对于确保Terraform操作的安全性至关重要

  • 比较了S3原生锁定功能与原有的S3 + DynamoDB组合方案

  • 使用S3原生锁定功能配置了新的Terraform后端环境,并设置了正确的存储桶参数

  • 成功地将现有的基于S3 + DynamoDB的后端环境迁移到了新方案中

  • 学会了如何验证锁定状态、处理锁住的情况,以及在必要时进行回滚操作

  • 为状态存储桶应用了安全最佳实践

这种利用S3原生锁定机制的方法,是今后在AWS上创建所有新的Terraform项目时所推荐的方案。如果你负责管理包含多个Terraform后端的庞大基础设施系统,可以考虑通过编写脚本或使用Terraform模块来自动化这一迁移过程,从而确保这一机制能够应用于你所有的状态存储桶中。

如果你正在为一家初创企业构建或优化云基础设施,并且需要一份关于可供生产环境使用的Terraform模块、CI/CD流水线配置方案以及基础设施运维指南的完整参考资料,请查看 《初创企业DevOps实战指南》。该指南涵盖了从初始设置到确保系统具备生产环境可靠性的整个AWS基础设施生命周期管理流程。

参考资料

Comments are closed.