【问题标题】:How to import manual changes into Terraform remote state如何将手动更改导入 Terraform 远程状态
【发布时间】:2017-10-12 12:05:03
【问题描述】:

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

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

还有一些资源,比如 DB,随着新参数的添加而改变,我也需要导入它们。当我尝试导入这些更改时,它会说:

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.

任何帮助将不胜感激。谢谢。

【问题讨论】:

    标签: amazon-web-services terraform


    【解决方案1】:

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

    在幕后,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 对其与底层系统关系的记录,如果该文件的内容丢失,则很难恢复。简单地替换资源通常比完成所有这些工作更容易,除非它已经在您的基础架构中发挥关键作用并且没有可用的优雅迁移策略。

    与现有资源冲突的导入

    您的问题中给出的错误消息是关于与现有资源“冲突”的导入:

    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 “忘记”资源;它仍然存在于 EC2 中,需要通过控制台、命令行工具或 API 手动删除。请务必在删除之前记下其id,以确保您可以找到它以便清理它。

    【讨论】:

    • 对此的一个旁注是,由于某种原因,Windows 版本的 terraform 将一些不可打印的 ascii 字符放入状态文件的拉取中,因此我不得不将所有 terraform 移动到 linux 框并在那里进行修改。
    【解决方案2】:

    对于资源,您必须使用导入功能或手动添加 terraform state like block..

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

    【讨论】:

    • 我能够进行数据库更改,您知道如何导入 aws_vpc_dhcp_options_association 吗?你能解释一下像块这样的 terraform 状态吗?如何做到这一点。
    • 此外,terraform 刷新将从我的远程状态匹配,但我的远程状态仅不会随着手动更改而更新。
    • Terraform 在不同情况下的行为不同.. 如果您想要特定的 dhcp 选项集解决方法.. 值得刷新 vpc 远程状态.. 它会将当前的 dhcp 选项集标记为默认值..
    猜你喜欢
    • 2021-09-25
    • 2019-11-14
    • 2016-11-24
    • 2019-05-29
    • 2016-12-22
    • 2021-01-19
    • 1970-01-01
    • 2021-12-11
    • 2019-07-28
    相关资源
    最近更新 更多