如何将手动更改导入 Terraform 远程状态

How to import manual changes into Terraform remote state

我是 terraform 的新手 - 我在 s3 中创建了远程 tfstate,现在我的 AWS 基础设施中也进行了一些手动更改。我需要将这些手动更改导入 tfstate。

有些资源我使用了导入命令,但是有些资源如IAM策略等,没有这样的导入命令。

另外一些资源如数据库也随着新参数的添加而改变,我也需要导入它们。当我尝试导入这些更改时,它说:

Error importing: 1 error(s) occurred:

* Can't import aws_security_group.Q8SgProdAdminSshInt, would collide
  with an existing resource.

Please remove or rename this resource before continuing.

如有任何帮助,我们将不胜感激。谢谢

对于资源,您必须使用导入功能或手动添加方块等地形状态。

或者如果配置有任何变化,就像你提到的 dB 配置。如果 dB 资源由 terraform 远程状态管理。terraform refresh 会帮助你......

在直接回答这个问题之前,我认为一些上下文会有所帮助:

在幕后,Terraform 维护一个 状态文件 ,其中包含从配置中的资源到底层提供程序中的对象的映射 API。当您使用 Terraform 创建新对象时,创建的对象的 id 会自动保存在状态中,以便将来的命令可以找到引用的对象以进行读取、更新和删除操作。

terraform import 那么,是在状态文件中创建条目的另一种方法。用户不是创建一个新对象并记录它的 id,而是在命令行上提供一个 id。 Terraform 读取 具有该 ID 的对象并将结果添加到状态文件,之后它在状态中与 Terraform 自己创建的资源无法区分。

综上所述,让我们一一解答您的问题。


导入不支持的资源terraform import

由于每个资源都需要少量的验证和数据获取代码来进行导入,因此目前并非所有资源都支持导入。

根据我们对上述 terraform import 所做的了解,理论上 可以跳过 Terraform 对提供的 ID 的验证,而是手动将资源添加到状态。 这是一项高级操作,必须小心操作以避免破坏状态

首先,将状态检索到您将用于本地工作的本地文件中:

terraform state pull >manual-import.tfstate

这将创建一个文件 manual-import.tfstate,您可以在文本编辑器中打开该文件。它使用 JSON 语法,因此尽管其内部结构未记录为稳定格式,但只要我们与预期结构保持一致,我们就可以仔细编辑它。

最简单的方法是找到与要导入的模块位于同一模块中的现有资源,然后复制和编辑它。假设我们有一个像这样的 resources 对象:

"resources": {
    "null_resource.foo": {
        "type": "null_resource",
        "depends_on": [],
        "primary": {
            "id": "5897853859325638329",
            "attributes": {
                "id": "5897853859325638329"
            },
            "meta": {},
            "tainted": false
        },
        "deposed": [],
        "provider": ""
    }
},

resources 对象中的每个属性对应于您配置中的一个资源。属性名称是资源的类型和名称。在本例中,资源类型为 null_resource,属性名称为 foo。在您的情况下,您可能会在此处看到类似 aws_instance.server 的内容。

对于许多资源(但不是全部!),id 属性是需要填充的主要内容。因此,我们可以为假设的 IAM 策略复制此结构:

"resources": {
    "null_resource.foo": {
        "type": "null_resource",
        "depends_on": [],
        "primary": {
            "id": "5897853859325638329",
            "attributes": {
                "id": "5897853859325638329"
            },
            "meta": {},
            "tainted": false
        },
        "deposed": [],
        "provider": ""
    },
    "aws_iam_policy.example": {
        "type": "aws_iam_policy",
        "depends_on": [],
        "primary": {
            "id": "?????",
            "attributes": {
                "id": "?????"
            },
            "meta": {},
            "tainted": false
        },
        "deposed": [],
        "provider": ""
    }
},

这一步的挑战是弄清楚这个资源需要什么样的 ID。知道这一点的唯一可靠方法是 read the code,它告诉我该资源期望 id 是策略的完整 ARN。

有了这些知识,我们将上面示例中的两个 ????? 序列替换为我们要导入的策略的 ARN。

手动更改状态后,有必要更新文件顶层的 serial 编号。 Terraform 期望任何新的更改都会有一个更高的序列号,因此我们可以增加这个数字。

完成更新后,我们必须将更新后的状态文件上传回 Terraform:

terraform state push manual-import.tfstate

最后我们可以要求 Terraform 刷新状态以确保它有效:

terraform refresh

同样,这是一个相当冒险的过程,因为状态文件是 Terraform 与底层系统关系的记录,如果此文件的内容丢失,则很难恢复。简单地替换资源通常比进行所有这些工作更容易,除非它已经在您的基础架构中发挥关键作用并且没有可用的优雅迁移策略。

导入与现有资源冲突

您问题中给出的错误消息是关于导入 "colliding" 现有资源:

Error importing: 1 error(s) occurred:

* Can't import aws_security_group.Q8SgProdAdminSshInt, would collide with an existing resource.

Please remove or rename this resource before continuing.

此消息的意思是,当 Terraform 尝试将新资源写入状态文件时,它发现名称 aws_security_group.Q8SgProdAdminSshInt 的资源条目已经存在。这表明它已经被导入或者 Terraform 本身已经创建了一个新的安全组。

您可以检查状态中现有资源的属性:

terraform state show aws_security_group.Q8SgProdAdminSshInt

将返回的数据与您尝试导入的安全组进行比较。如果 ID 匹配,则无需执行任何操作,因为资源已导入。

如果 ID 匹配,那么您需要弄清楚这两个对象中的哪一个是您要保留的对象。如果您想保留 Terraform 已有的那个,您可以手动删除您尝试导入的那个。

如果您想保留您尝试导入的那个,您可以从 Terraform 状态中删除不需要的那个,以便让导入成功:

terraform state rm aws_security_group.Q8SgProdAdminSshInt

请注意,这只会使 Terraform "forget" 成为资源;它仍将存在于 EC2 中,需要通过控制台、命令行工具或 API 手动删除。请务必在删除它之前记下它的id,以确保您可以找到它以便清理它。